From ec4bc27b46b48aeba3ca79cc0dc55ca3a7e12a54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Boris=20B=C3=BCgling?= Date: Tue, 13 Feb 2024 12:03:58 -0800 Subject: [PATCH 001/159] Don't lock if we don't have a package root (#7335) It seems like for reasons we have a fallback scratch directory even if we couldn't determine a package root, but it doesn't seem as if we should lock that since it is likely not the scratch directory we want. --- Sources/CoreCommands/SwiftTool.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Sources/CoreCommands/SwiftTool.swift b/Sources/CoreCommands/SwiftTool.swift index 3f20f707b8e..eb5e57af1b1 100644 --- a/Sources/CoreCommands/SwiftTool.swift +++ b/Sources/CoreCommands/SwiftTool.swift @@ -901,6 +901,9 @@ public final class SwiftTool { } fileprivate func acquireLockIfNeeded() throws { + guard packageRoot != nil else { + return + } assert(workspaceLockState == .needsLocking, "attempting to `acquireLockIfNeeded()` from unexpected state: \(workspaceLockState)") guard workspaceLock == nil else { throw InternalError("acquireLockIfNeeded() called multiple times") From 78f858a28f876603d2fe6d0f7b2bea2ba74770ef Mon Sep 17 00:00:00 2001 From: Rauhul Varma Date: Tue, 13 Feb 2024 14:23:43 -0800 Subject: [PATCH 002/159] Move and refactor `ProgressAnimation` code from TSC (#7328) SwiftPM uses ProgressAnimation from TSCUtilities in a variety of inconsistent manners across various swift-something commands. This commit replaces the uses of ProgressAnimation throughout SwiftPM with common entrypoints and lays the ground work for creating multi-line parallel progress animations as seen in tools like `bazel build` and `docker build`. --------- Co-authored-by: Max Desiatov --- Sources/Basics/CMakeLists.txt | 6 +- .../NinjaProgressAnimation.swift | 102 +++++++++++++ .../PercentProgressAnimation.swift | 139 ++++++++++++++++++ .../ProgressAnimationProtocol.swift | 59 ++++++++ .../SingleLinePercentProgressAnimation.swift | 55 +++++++ .../ThrottledProgressAnimation.swift} | 47 ++++-- Sources/Build/BuildOperation.swift | 12 +- ...dOperationBuildSystemDelegateHandler.swift | 2 +- Sources/Commands/SwiftTestTool.swift | 16 +- Sources/CoreCommands/SwiftTool.swift | 4 - .../SwiftToolObservabilityHandler.swift | 13 +- .../SwiftSDKs/SwiftSDKBundleStore.swift | 1 + Sources/SwiftSDKTool/InstallSwiftSDK.swift | 7 +- Sources/XCBuildSupport/XCBuildDelegate.swift | 3 +- Sources/XCBuildSupport/XcodeBuildSystem.swift | 11 +- .../BasicsTests/ProgressAnimationTests.swift | 1 - 16 files changed, 424 insertions(+), 54 deletions(-) create mode 100644 Sources/Basics/ProgressAnimation/NinjaProgressAnimation.swift create mode 100644 Sources/Basics/ProgressAnimation/PercentProgressAnimation.swift create mode 100644 Sources/Basics/ProgressAnimation/ProgressAnimationProtocol.swift create mode 100644 Sources/Basics/ProgressAnimation/SingleLinePercentProgressAnimation.swift rename Sources/Basics/{ProgressAnimation.swift => ProgressAnimation/ThrottledProgressAnimation.swift} (62%) diff --git a/Sources/Basics/CMakeLists.txt b/Sources/Basics/CMakeLists.txt index 777de1a98ed..eec426c2fe9 100644 --- a/Sources/Basics/CMakeLists.txt +++ b/Sources/Basics/CMakeLists.txt @@ -51,7 +51,11 @@ add_library(Basics Netrc.swift Observability.swift OSSignpost.swift - ProgressAnimation.swift + ProgressAnimation/NinjaProgressAnimation.swift + ProgressAnimation/PercentProgressAnimation.swift + ProgressAnimation/ProgressAnimationProtocol.swift + ProgressAnimation/SingleLinePercentProgressAnimation.swift + ProgressAnimation/ThrottledProgressAnimation.swift SQLite.swift Sandbox.swift SendableTimeInterval.swift diff --git a/Sources/Basics/ProgressAnimation/NinjaProgressAnimation.swift b/Sources/Basics/ProgressAnimation/NinjaProgressAnimation.swift new file mode 100644 index 00000000000..5a42adec7c0 --- /dev/null +++ b/Sources/Basics/ProgressAnimation/NinjaProgressAnimation.swift @@ -0,0 +1,102 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import class TSCBasic.TerminalController +import protocol TSCBasic.WritableByteStream + +extension ProgressAnimation { + /// A ninja-like progress animation that adapts to the provided output stream. + @_spi(SwiftPMInternal) + public static func ninja( + stream: WritableByteStream, + verbose: Bool + ) -> any ProgressAnimationProtocol { + Self.dynamic( + stream: stream, + verbose: verbose, + ttyTerminalAnimationFactory: { RedrawingNinjaProgressAnimation(terminal: $0) }, + dumbTerminalAnimationFactory: { SingleLinePercentProgressAnimation(stream: stream, header: nil) }, + defaultAnimationFactory: { MultiLineNinjaProgressAnimation(stream: stream) } + ) + } +} + +/// A redrawing ninja-like progress animation. +final class RedrawingNinjaProgressAnimation: ProgressAnimationProtocol { + private let terminal: TerminalController + private var hasDisplayedProgress = false + + init(terminal: TerminalController) { + self.terminal = terminal + } + + func update(step: Int, total: Int, text: String) { + assert(step <= total) + + terminal.clearLine() + + let progressText = "[\(step)/\(total)] \(text)" + let width = terminal.width + if progressText.utf8.count > width { + let suffix = "…" + terminal.write(String(progressText.prefix(width - suffix.utf8.count))) + terminal.write(suffix) + } else { + terminal.write(progressText) + } + + hasDisplayedProgress = true + } + + func complete(success: Bool) { + if hasDisplayedProgress { + terminal.endLine() + } + } + + func clear() { + terminal.clearLine() + } +} + +/// A multi-line ninja-like progress animation. +final class MultiLineNinjaProgressAnimation: ProgressAnimationProtocol { + private struct Info: Equatable { + let step: Int + let total: Int + let text: String + } + + private let stream: WritableByteStream + private var lastDisplayedText: String? = nil + + init(stream: WritableByteStream) { + self.stream = stream + } + + func update(step: Int, total: Int, text: String) { + assert(step <= total) + + guard text != lastDisplayedText else { return } + + stream.send("[\(step)/\(total)] ").send(text) + stream.send("\n") + stream.flush() + lastDisplayedText = text + } + + func complete(success: Bool) { + } + + func clear() { + } +} diff --git a/Sources/Basics/ProgressAnimation/PercentProgressAnimation.swift b/Sources/Basics/ProgressAnimation/PercentProgressAnimation.swift new file mode 100644 index 00000000000..0020b10df86 --- /dev/null +++ b/Sources/Basics/ProgressAnimation/PercentProgressAnimation.swift @@ -0,0 +1,139 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import class TSCBasic.TerminalController +import protocol TSCBasic.WritableByteStream + +extension ProgressAnimation { + /// A percent-based progress animation that adapts to the provided output stream. + @_spi(SwiftPMInternal) + public static func percent( + stream: WritableByteStream, + verbose: Bool, + header: String + ) -> any ProgressAnimationProtocol { + Self.dynamic( + stream: stream, + verbose: verbose, + ttyTerminalAnimationFactory: { RedrawingPercentProgressAnimation(terminal: $0, header: header) }, + dumbTerminalAnimationFactory: { SingleLinePercentProgressAnimation(stream: stream, header: header) }, + defaultAnimationFactory: { MultiLinePercentProgressAnimation(stream: stream, header: header) } + ) + } +} + +/// A redrawing lit-like progress animation. +final class RedrawingPercentProgressAnimation: ProgressAnimationProtocol { + private let terminal: TerminalController + private let header: String + private var hasDisplayedHeader = false + + init(terminal: TerminalController, header: String) { + self.terminal = terminal + self.header = header + } + + /// Creates repeating string for count times. + /// If count is negative, returns empty string. + private func repeating(string: String, count: Int) -> String { + return String(repeating: string, count: max(count, 0)) + } + + func update(step: Int, total: Int, text: String) { + assert(step <= total) + + let width = terminal.width + if !hasDisplayedHeader { + let spaceCount = width / 2 - header.utf8.count / 2 + terminal.write(repeating(string: " ", count: spaceCount)) + terminal.write(header, inColor: .cyan, bold: true) + terminal.endLine() + hasDisplayedHeader = true + } else { + terminal.moveCursor(up: 1) + } + + terminal.clearLine() + let percentage = step * 100 / total + let paddedPercentage = percentage < 10 ? " \(percentage)" : "\(percentage)" + let prefix = "\(paddedPercentage)% " + terminal.wrap("[", inColor: .green, bold: true) + terminal.write(prefix) + + let barWidth = width - prefix.utf8.count + let n = Int(Double(barWidth) * Double(percentage) / 100.0) + + terminal.write(repeating(string: "=", count: n) + repeating(string: "-", count: barWidth - n), inColor: .green) + terminal.write("]", inColor: .green, bold: true) + terminal.endLine() + + terminal.clearLine() + if text.utf8.count > width { + let prefix = "…" + terminal.write(prefix) + terminal.write(String(text.suffix(width - prefix.utf8.count))) + } else { + terminal.write(text) + } + } + + func complete(success: Bool) { + terminal.endLine() + terminal.endLine() + } + + func clear() { + terminal.clearLine() + terminal.moveCursor(up: 1) + terminal.clearLine() + } +} + +/// A multi-line percent-based progress animation. +final class MultiLinePercentProgressAnimation: ProgressAnimationProtocol { + private struct Info: Equatable { + let percentage: Int + let text: String + } + + private let stream: WritableByteStream + private let header: String + private var hasDisplayedHeader = false + private var lastDisplayedText: String? = nil + + init(stream: WritableByteStream, header: String) { + self.stream = stream + self.header = header + } + + func update(step: Int, total: Int, text: String) { + assert(step <= total) + + if !hasDisplayedHeader, !header.isEmpty { + stream.send(header) + stream.send("\n") + stream.flush() + hasDisplayedHeader = true + } + + let percentage = step * 100 / total + stream.send("\(percentage)%: ").send(text) + stream.send("\n") + stream.flush() + lastDisplayedText = text + } + + func complete(success: Bool) { + } + + func clear() { + } +} diff --git a/Sources/Basics/ProgressAnimation/ProgressAnimationProtocol.swift b/Sources/Basics/ProgressAnimation/ProgressAnimationProtocol.swift new file mode 100644 index 00000000000..2a596271c8c --- /dev/null +++ b/Sources/Basics/ProgressAnimation/ProgressAnimationProtocol.swift @@ -0,0 +1,59 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import class TSCBasic.TerminalController +import class TSCBasic.LocalFileOutputByteStream +import protocol TSCBasic.WritableByteStream +import protocol TSCUtility.ProgressAnimationProtocol + +@_spi(SwiftPMInternal) +public typealias ProgressAnimationProtocol = TSCUtility.ProgressAnimationProtocol + +/// Namespace to nest public progress animations under. +@_spi(SwiftPMInternal) +public enum ProgressAnimation { + /// Dynamically create a progress animation based on the current stream + /// capabilities and desired verbosity. + /// + /// - Parameters: + /// - stream: A stream to write animations into. + /// - verbose: The verbosity level of other output in the system. + /// - ttyTerminalAnimationFactory: A progress animation to use when the + /// output stream is connected to a terminal with support for special + /// escape sequences. + /// - dumbTerminalAnimationFactory: A progress animation to use when the + /// output stream is connected to a terminal without support for special + /// escape sequences for clearing lines or controlling cursor positions. + /// - defaultAnimationFactory: A progress animation to use when the + /// desired output is verbose or the output stream verbose or is not + /// connected to a terminal, e.g. a pipe or file. + /// - Returns: A progress animation instance matching the stream + /// capabilities and desired verbosity. + static func dynamic( + stream: WritableByteStream, + verbose: Bool, + ttyTerminalAnimationFactory: (TerminalController) -> any ProgressAnimationProtocol, + dumbTerminalAnimationFactory: () -> any ProgressAnimationProtocol, + defaultAnimationFactory: () -> any ProgressAnimationProtocol + ) -> any ProgressAnimationProtocol { + if let terminal = TerminalController(stream: stream), !verbose { + return ttyTerminalAnimationFactory(terminal) + } else if let fileStream = stream as? LocalFileOutputByteStream, + TerminalController.terminalType(fileStream) == .dumb + { + return dumbTerminalAnimationFactory() + } else { + return defaultAnimationFactory() + } + } +} + diff --git a/Sources/Basics/ProgressAnimation/SingleLinePercentProgressAnimation.swift b/Sources/Basics/ProgressAnimation/SingleLinePercentProgressAnimation.swift new file mode 100644 index 00000000000..c11b25e4b9b --- /dev/null +++ b/Sources/Basics/ProgressAnimation/SingleLinePercentProgressAnimation.swift @@ -0,0 +1,55 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import class TSCBasic.TerminalController +import protocol TSCBasic.WritableByteStream + +/// A single line percent-based progress animation. +final class SingleLinePercentProgressAnimation: ProgressAnimationProtocol { + private let stream: WritableByteStream + private let header: String? + private var displayedPercentages: Set = [] + private var hasDisplayedHeader = false + + init(stream: WritableByteStream, header: String?) { + self.stream = stream + self.header = header + } + + func update(step: Int, total: Int, text: String) { + if let header = header, !hasDisplayedHeader { + stream.send(header) + stream.send("\n") + stream.flush() + hasDisplayedHeader = true + } + + let percentage = step * 100 / total + let roundedPercentage = Int(Double(percentage / 10).rounded(.down)) * 10 + if percentage != 100, !displayedPercentages.contains(roundedPercentage) { + stream.send(String(roundedPercentage)).send(".. ") + displayedPercentages.insert(roundedPercentage) + } + + stream.flush() + } + + func complete(success: Bool) { + if success { + stream.send("OK") + stream.flush() + } + } + + func clear() { + } +} diff --git a/Sources/Basics/ProgressAnimation.swift b/Sources/Basics/ProgressAnimation/ThrottledProgressAnimation.swift similarity index 62% rename from Sources/Basics/ProgressAnimation.swift rename to Sources/Basics/ProgressAnimation/ThrottledProgressAnimation.swift index 9be0dc93c5e..30f006c873a 100644 --- a/Sources/Basics/ProgressAnimation.swift +++ b/Sources/Basics/ProgressAnimation/ThrottledProgressAnimation.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2024 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information @@ -11,23 +11,13 @@ //===----------------------------------------------------------------------===// import _Concurrency -import protocol TSCUtility.ProgressAnimationProtocol /// A progress animation wrapper that throttles updates to a given interval. -@_spi(SwiftPMInternal) -public class ThrottledProgressAnimation: ProgressAnimationProtocol { +final class ThrottledProgressAnimation: ProgressAnimationProtocol { private let animation: ProgressAnimationProtocol private let shouldUpdate: () -> Bool private var pendingUpdate: (Int, Int, String)? - public convenience init(_ animation: ProgressAnimationProtocol, interval: ContinuousClock.Duration) { - self.init(animation, clock: ContinuousClock(), interval: interval) - } - - public convenience init(_ animation: ProgressAnimationProtocol, clock: C, interval: C.Duration) { - self.init(animation, now: { clock.now }, interval: interval, clock: C.self) - } - init( _ animation: ProgressAnimationProtocol, now: @escaping () -> C.Instant, interval: C.Duration, clock: C.Type = C.self @@ -45,7 +35,7 @@ public class ThrottledProgressAnimation: ProgressAnimationProtocol { } } - public func update(step: Int, total: Int, text: String) { + func update(step: Int, total: Int, text: String) { guard shouldUpdate() else { pendingUpdate = (step, total, text) return @@ -54,14 +44,41 @@ public class ThrottledProgressAnimation: ProgressAnimationProtocol { animation.update(step: step, total: total, text: text) } - public func complete(success: Bool) { + func complete(success: Bool) { if let (step, total, text) = pendingUpdate { animation.update(step: step, total: total, text: text) } animation.complete(success: success) } - public func clear() { + func clear() { animation.clear() } } + +@_spi(SwiftPMInternal) +extension ProgressAnimationProtocol { + @_spi(SwiftPMInternal) + public func throttled( + now: @escaping () -> C.Instant, + interval: C.Duration, + clock: C.Type = C.self + ) -> some ProgressAnimationProtocol { + ThrottledProgressAnimation(self, now: now, interval: interval, clock: clock) + } + + @_spi(SwiftPMInternal) + public func throttled( + clock: C, + interval: C.Duration + ) -> some ProgressAnimationProtocol { + self.throttled(now: { clock.now }, interval: interval, clock: C.self) + } + + @_spi(SwiftPMInternal) + public func throttled( + interval: ContinuousClock.Duration + ) -> some ProgressAnimationProtocol { + self.throttled(clock: ContinuousClock(), interval: interval) + } +} diff --git a/Sources/Build/BuildOperation.swift b/Sources/Build/BuildOperation.swift index 65f7d9e5c8f..2342f8ddc47 100644 --- a/Sources/Build/BuildOperation.swift +++ b/Sources/Build/BuildOperation.swift @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +@_spi(SwiftPMInternal) import Basics import LLBuildManifest import PackageGraph @@ -26,9 +27,6 @@ import enum TSCBasic.ProcessEnv import struct TSCBasic.RegEx import enum TSCUtility.Diagnostics -import class TSCUtility.MultiLineNinjaProgressAnimation -import class TSCUtility.NinjaProgressAnimation -import protocol TSCUtility.ProgressAnimationProtocol #if USE_IMPL_ONLY_IMPORTS @_implementationOnly import DriverSupport @@ -609,10 +607,10 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS /// building the package structure target. private func createBuildSystem(buildDescription: BuildDescription?) throws -> SPMLLBuild.BuildSystem { // Figure out which progress bar we have to use during the build. - let progressAnimation: ProgressAnimationProtocol = self.logLevel.isVerbose - ? MultiLineNinjaProgressAnimation(stream: self.outputStream) - : NinjaProgressAnimation(stream: self.outputStream) - + let progressAnimation = ProgressAnimation.ninja( + stream: self.outputStream, + verbose: self.logLevel.isVerbose + ) let buildExecutionContext = BuildExecutionContext( productsBuildParameters: self.productsBuildParameters, toolsBuildParameters: self.toolsBuildParameters, diff --git a/Sources/Build/BuildOperationBuildSystemDelegateHandler.swift b/Sources/Build/BuildOperationBuildSystemDelegateHandler.swift index 482235fe985..47adeef8179 100644 --- a/Sources/Build/BuildOperationBuildSystemDelegateHandler.swift +++ b/Sources/Build/BuildOperationBuildSystemDelegateHandler.swift @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +@_spi(SwiftPMInternal) import Basics import Dispatch import Foundation @@ -28,7 +29,6 @@ import class TSCBasic.ThreadSafeOutputByteStream import class TSCUtility.IndexStore import class TSCUtility.IndexStoreAPI -import protocol TSCUtility.ProgressAnimationProtocol #if canImport(llbuildSwift) typealias LLBuildBuildSystemDelegate = llbuildSwift.BuildSystemDelegate diff --git a/Sources/Commands/SwiftTestTool.swift b/Sources/Commands/SwiftTestTool.swift index 806ec17d09f..1a713702f43 100644 --- a/Sources/Commands/SwiftTestTool.swift +++ b/Sources/Commands/SwiftTestTool.swift @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// import ArgumentParser +@_spi(SwiftPMInternal) import Basics import CoreCommands import Dispatch @@ -29,10 +30,6 @@ import var TSCBasic.stdoutStream import class TSCBasic.SynchronizedQueue import class TSCBasic.Thread -import class TSCUtility.NinjaProgressAnimation -import class TSCUtility.PercentProgressAnimation -import protocol TSCUtility.ProgressAnimationProtocol - private enum TestError: Swift.Error { case invalidListTestJSONData(context: String, underlyingError: Error? = nil) case testsNotFound @@ -930,9 +927,16 @@ final class ParallelTestRunner { // command's result output goes on stdout // ie "swift test" should output to stdout if ProcessEnv.vars["SWIFTPM_TEST_RUNNER_PROGRESS_BAR"] == "lit" { - progressAnimation = PercentProgressAnimation(stream: TSCBasic.stdoutStream, header: "Testing:") + self.progressAnimation = ProgressAnimation.percent( + stream: TSCBasic.stdoutStream, + verbose: false, + header: "Testing:" + ) } else { - progressAnimation = NinjaProgressAnimation(stream: TSCBasic.stdoutStream) + self.progressAnimation = ProgressAnimation.ninja( + stream: TSCBasic.stdoutStream, + verbose: false + ) } self.buildOptions = buildOptions diff --git a/Sources/CoreCommands/SwiftTool.swift b/Sources/CoreCommands/SwiftTool.swift index eb5e57af1b1..5d4bfc0a02b 100644 --- a/Sources/CoreCommands/SwiftTool.swift +++ b/Sources/CoreCommands/SwiftTool.swift @@ -48,10 +48,6 @@ import var TSCBasic.stderrStream import class TSCBasic.TerminalController import class TSCBasic.ThreadSafeOutputByteStream -import class TSCUtility.MultiLineNinjaProgressAnimation -import class TSCUtility.NinjaProgressAnimation -import class TSCUtility.PercentProgressAnimation -import protocol TSCUtility.ProgressAnimationProtocol import var TSCUtility.verbosity typealias Diagnostic = Basics.Diagnostic diff --git a/Sources/CoreCommands/SwiftToolObservabilityHandler.swift b/Sources/CoreCommands/SwiftToolObservabilityHandler.swift index 2c82fe21912..765c2f0cd5c 100644 --- a/Sources/CoreCommands/SwiftToolObservabilityHandler.swift +++ b/Sources/CoreCommands/SwiftToolObservabilityHandler.swift @@ -10,7 +10,7 @@ // //===----------------------------------------------------------------------===// - +@_spi(SwiftPMInternal) import Basics import Dispatch @@ -18,10 +18,6 @@ import protocol TSCBasic.OutputByteStream import class TSCBasic.TerminalController import class TSCBasic.ThreadSafeOutputByteStream -import class TSCUtility.MultiLineNinjaProgressAnimation -import class TSCUtility.NinjaProgressAnimation -import protocol TSCUtility.ProgressAnimationProtocol - public struct SwiftToolObservabilityHandler: ObservabilityHandlerProvider { private let outputHandler: OutputHandler @@ -76,9 +72,10 @@ public struct SwiftToolObservabilityHandler: ObservabilityHandlerProvider { self.logLevel = logLevel self.outputStream = outputStream self.writer = InteractiveWriter(stream: outputStream) - self.progressAnimation = logLevel.isVerbose ? - MultiLineNinjaProgressAnimation(stream: outputStream) : - NinjaProgressAnimation(stream: outputStream) + self.progressAnimation = ProgressAnimation.ninja( + stream: self.outputStream, + verbose: self.logLevel.isVerbose + ) } func handleDiagnostic(scope: ObservabilityScope, diagnostic: Basics.Diagnostic) { diff --git a/Sources/PackageModel/SwiftSDKs/SwiftSDKBundleStore.swift b/Sources/PackageModel/SwiftSDKs/SwiftSDKBundleStore.swift index f5e40bb2083..88c911853e2 100644 --- a/Sources/PackageModel/SwiftSDKs/SwiftSDKBundleStore.swift +++ b/Sources/PackageModel/SwiftSDKs/SwiftSDKBundleStore.swift @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// // FIXME: can't write `import actor Basics.HTTPClient`, importing the whole module because of that :( +@_spi(SwiftPMInternal) import Basics import struct Foundation.URL import protocol TSCBasic.FileSystem diff --git a/Sources/SwiftSDKTool/InstallSwiftSDK.swift b/Sources/SwiftSDKTool/InstallSwiftSDK.swift index 9ab54a0a8dd..39b8b4f2471 100644 --- a/Sources/SwiftSDKTool/InstallSwiftSDK.swift +++ b/Sources/SwiftSDKTool/InstallSwiftSDK.swift @@ -18,7 +18,6 @@ import Foundation import PackageModel import var TSCBasic.stdoutStream -import class TSCUtility.PercentProgressAnimation public struct InstallSwiftSDK: SwiftSDKSubcommand { public static let configuration = CommandConfiguration( @@ -50,9 +49,9 @@ public struct InstallSwiftSDK: SwiftSDKSubcommand { fileSystem: self.fileSystem, observabilityScope: observabilityScope, outputHandler: { print($0.description) }, - downloadProgressAnimation: ThrottledProgressAnimation( - PercentProgressAnimation(stream: stdoutStream, header: "Downloading"), interval: .milliseconds(300) - ) + downloadProgressAnimation: ProgressAnimation + .percent(stream: stdoutStream, verbose: false, header: "Downloading") + .throttled(interval: .milliseconds(300)) ) try await store.install( bundlePathOrURL: bundlePathOrURL, diff --git a/Sources/XCBuildSupport/XCBuildDelegate.swift b/Sources/XCBuildSupport/XCBuildDelegate.swift index f7c0ae13bf7..acf7f0c7317 100644 --- a/Sources/XCBuildSupport/XCBuildDelegate.swift +++ b/Sources/XCBuildSupport/XCBuildDelegate.swift @@ -145,9 +145,8 @@ private extension Basics.Diagnostic { } } -// FIXME: Move to TSC. +@available(*, deprecated, message: "use ProgressAnimation.ninja(stream:) instead") public final class VerboseProgressAnimation: ProgressAnimationProtocol { - private let stream: OutputByteStream public init(stream: OutputByteStream) { diff --git a/Sources/XCBuildSupport/XcodeBuildSystem.swift b/Sources/XCBuildSupport/XcodeBuildSystem.swift index 9eeb82ac32d..89b493e545c 100644 --- a/Sources/XCBuildSupport/XcodeBuildSystem.swift +++ b/Sources/XCBuildSupport/XcodeBuildSystem.swift @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +@_spi(SwiftPMInternal) import Basics import Dispatch import class Foundation.JSONEncoder @@ -23,9 +24,7 @@ import enum TSCBasic.ProcessEnv import func TSCBasic.withTemporaryFile import func TSCBasic.memoize -import class TSCUtility.MultiLinePercentProgressAnimation import enum TSCUtility.Diagnostics -import protocol TSCUtility.ProgressAnimationProtocol public final class XcodeBuildSystem: SPMBuildCore.BuildSystem { private let buildParameters: BuildParameters @@ -248,9 +247,11 @@ public final class XcodeBuildSystem: SPMBuildCore.BuildSystem { /// Returns a new instance of `XCBuildDelegate` for a build operation. private func createBuildDelegate() -> XCBuildDelegate { - let progressAnimation: ProgressAnimationProtocol = self.logLevel.isVerbose - ? VerboseProgressAnimation(stream: self.outputStream) - : MultiLinePercentProgressAnimation(stream: self.outputStream, header: "") + let progressAnimation = ProgressAnimation.percent( + stream: self.outputStream, + verbose: self.logLevel.isVerbose, + header: "" + ) let delegate = XCBuildDelegate( buildSystem: self, outputStream: self.outputStream, diff --git a/Tests/BasicsTests/ProgressAnimationTests.swift b/Tests/BasicsTests/ProgressAnimationTests.swift index 5ce1e372a86..b93ef46c2a9 100644 --- a/Tests/BasicsTests/ProgressAnimationTests.swift +++ b/Tests/BasicsTests/ProgressAnimationTests.swift @@ -12,7 +12,6 @@ import _Concurrency import XCTest -import protocol TSCUtility.ProgressAnimationProtocol @_spi(SwiftPMInternal) @testable From 9131d8de0ca2adc2da0b332d532edbfe8ecdbb63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Boris=20B=C3=BCgling?= Date: Wed, 14 Feb 2024 12:33:15 -0800 Subject: [PATCH 003/159] Add hidden `--ignore-lock` escape hatch (#7338) This is obviously dangerous, but arguably has been the default behavior of SwiftPM for many years, so offering an escape hatch seems appropriate. --- Sources/CoreCommands/Options.swift | 3 +++ Sources/CoreCommands/SwiftTool.swift | 15 ++++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Sources/CoreCommands/Options.swift b/Sources/CoreCommands/Options.swift index 8b558320371..e04e4e7ecfe 100644 --- a/Sources/CoreCommands/Options.swift +++ b/Sources/CoreCommands/Options.swift @@ -121,6 +121,9 @@ public struct LocationOptions: ParsableArguments { completion: .directory ) public var pkgConfigDirectories: [AbsolutePath] = [] + + @Option(name: .customLong("ignore-lock"), help: .hidden) + public var ignoreLock: Bool = false } public struct CachingOptions: ParsableArguments { diff --git a/Sources/CoreCommands/SwiftTool.swift b/Sources/CoreCommands/SwiftTool.swift index 5d4bfc0a02b..a23c80ff008 100644 --- a/Sources/CoreCommands/SwiftTool.swift +++ b/Sources/CoreCommands/SwiftTool.swift @@ -913,11 +913,16 @@ public final class SwiftTool { try workspaceLock.lock(type: .exclusive, blocking: false) } catch let ProcessLockError.unableToAquireLock(errno) { if errno == EWOULDBLOCK { - self.outputStream.write("Another instance of SwiftPM is already running using '\(self.scratchDirectory)', waiting until that process has finished execution...".utf8) - self.outputStream.flush() - - // Only if we fail because there's an existing lock we need to acquire again as blocking. - try workspaceLock.lock(type: .exclusive, blocking: true) + if self.options.locations.ignoreLock { + self.outputStream.write("Another instance of SwiftPM is already running using '\(self.scratchDirectory)', but this will be ignored since `--ignore-lock` has been passed".utf8) + self.outputStream.flush() + } else { + self.outputStream.write("Another instance of SwiftPM is already running using '\(self.scratchDirectory)', waiting until that process has finished execution...".utf8) + self.outputStream.flush() + + // Only if we fail because there's an existing lock we need to acquire again as blocking. + try workspaceLock.lock(type: .exclusive, blocking: true) + } } } From 4eb3684993cc21cf6335969195332eb11b617e36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Boris=20B=C3=BCgling?= Date: Thu, 15 Feb 2024 04:44:59 -0800 Subject: [PATCH 004/159] Bump swift-syntax version in the macro template (#7339) We use the "-latest" signifier to avoid confusion here, but basically this means we'll use any "-prerelease" tags and also the final release once it is out. --- Utilities/config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Utilities/config.json b/Utilities/config.json index 8638fc1f588..5e693bf466e 100644 --- a/Utilities/config.json +++ b/Utilities/config.json @@ -1 +1 @@ -{"version":1,"swiftSyntaxVersionForMacroTemplate":{"major":509,"minor":0,"patch":0}} \ No newline at end of file +{"version":1,"swiftSyntaxVersionForMacroTemplate":{"major":510,"minor":0,"patch":0, "prereleaseIdentifier":"latest"}} \ No newline at end of file From b704f856d0959c8e34e236980986d33ee898cf56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Boris=20B=C3=BCgling?= Date: Thu, 15 Feb 2024 09:13:40 -0800 Subject: [PATCH 005/159] Remove `@neonichu` from CODEOWNERS (#7342) Seems like a good time to ship this since I am wrapping up my last few PRs and this way I don't get added as reviewer to new PRs which will set the correct expectation that I won't be doing any future code reviews. --- CODEOWNERS | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 4ddc2dd2776..3d5fc3efd50 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -8,10 +8,6 @@ # (W), PGP key ID and fingerprint (P), description (D), and snail-mail address # (S). -# N: Boris Buegling -# E: bbuegling@apple.com -# D: Package Manager - # N: Tomer Doron # E: tomer@apple.com # D: Package Manager @@ -28,4 +24,4 @@ # The following lines are used by GitHub to automatically recommend reviewers. -* @bnbarham @MaxDesiatov @neonichu @tomerd +* @bnbarham @MaxDesiatov @tomerd From a1b4ad7cbc8162da463028e7c02c6fd885a10ee0 Mon Sep 17 00:00:00 2001 From: Dan Mendoza Date: Thu, 15 Feb 2024 11:39:24 -0800 Subject: [PATCH 006/159] Add option to allow HTTP (not HTTPS) registries (#7204) Addresses #7170: this adds an option to the `package-registry` `set` and `publish` commands to allow using an HTTP registry endpoint, bypassing the existing requirement that package registries use HTTPS. ### Motivation: In short, the motivation is to allow publishing to a registry that is hosted on a local machine. It is difficult to host a fully TLS-compliant HTTPS endpoint on developer machines at scale. See more detail in #7170 ### Modifications: * Adds a new `allowInsecureHTTP` flag to the Registry model. * Adds a new `--allow-insecure-http` flag to the `package-registry` `set` and `publish` commands. * Adds tests that validate the behavior of the model and command additions. * Adds tests that validate the requested constraints in #7170 regarding HTTP endpoints, such as not being able to publish to an HTTP endpoint if there is authentication configured for it, and `login` command requiring HTTPS. ### Result: We will be able to use the `swift` CLI to publish to our HTTP registries locally hosted on our development machines. As a result, we will be able to migrate to the official tooling instead of needing to continue using home-rolled publish tooling. --- .../RegistryConfiguration.swift | 6 +- .../PackageRegistryTool+Publish.swift | 6 +- .../PackageRegistryTool.swift | 14 +- .../PackageRegistryToolTests.swift | 194 +++++++++++++++++- .../RegistryConfigurationTests.swift | 2 +- 5 files changed, 211 insertions(+), 11 deletions(-) diff --git a/Sources/PackageRegistry/RegistryConfiguration.swift b/Sources/PackageRegistry/RegistryConfiguration.swift index 642e60a281d..8b6a15d5647 100644 --- a/Sources/PackageRegistry/RegistryConfiguration.swift +++ b/Sources/PackageRegistry/RegistryConfiguration.swift @@ -405,9 +405,9 @@ extension PackageModel.Registry: Codable { public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - self.init( - url: try container.decode(URL.self, forKey: .url), - supportsAvailability: try container.decodeIfPresent(Bool.self, forKey: .supportsAvailability) ?? false + try self.init( + url: container.decode(URL.self, forKey: .url), + supportsAvailability: container.decodeIfPresent(Bool.self, forKey: .supportsAvailability) ?? false ) } diff --git a/Sources/PackageRegistryTool/PackageRegistryTool+Publish.swift b/Sources/PackageRegistryTool/PackageRegistryTool+Publish.swift index d049340aff7..b688d824772 100644 --- a/Sources/PackageRegistryTool/PackageRegistryTool+Publish.swift +++ b/Sources/PackageRegistryTool/PackageRegistryTool+Publish.swift @@ -82,6 +82,9 @@ extension SwiftPackageRegistryTool { ) var certificateChainPaths: [AbsolutePath] = [] + @Flag(name: .customLong("allow-insecure-http"), help: "Allow using a non-HTTPS registry URL") + var allowInsecureHTTP: Bool = false + @Flag(help: "Dry run only; prepare the archive and sign it but do not publish to the registry.") var dryRun: Bool = false @@ -106,7 +109,8 @@ extension SwiftPackageRegistryTool { throw ValidationError.unknownRegistry } - try registryURL.validateRegistryURL() + let allowHTTP = try self.allowInsecureHTTP && (configuration.authentication(for: registryURL) == nil) + try registryURL.validateRegistryURL(allowHTTP: allowHTTP) // validate working directory path if let customWorkingDirectory { diff --git a/Sources/PackageRegistryTool/PackageRegistryTool.swift b/Sources/PackageRegistryTool/PackageRegistryTool.swift index 4f218ac1733..b387f6c1dfa 100644 --- a/Sources/PackageRegistryTool/PackageRegistryTool.swift +++ b/Sources/PackageRegistryTool/PackageRegistryTool.swift @@ -54,6 +54,9 @@ public struct SwiftPackageRegistryTool: AsyncParsableCommand { @Option(help: "Associate the registry with a given scope") var scope: String? + @Flag(name: .customLong("allow-insecure-http"), help: "Allow using a non-HTTPS registry URL") + var allowInsecureHTTP: Bool = false + @Argument(help: "The registry URL") var url: URL @@ -62,15 +65,16 @@ public struct SwiftPackageRegistryTool: AsyncParsableCommand { } func run(_ swiftTool: SwiftTool) async throws { - try self.registryURL.validateRegistryURL() + try self.registryURL.validateRegistryURL(allowHTTP: self.allowInsecureHTTP) let scope = try scope.map(PackageIdentity.Scope.init(validating:)) let set: (inout RegistryConfiguration) throws -> Void = { configuration in + let registry = Registry(url: self.registryURL, supportsAvailability: false) if let scope { - configuration.scopedRegistries[scope] = .init(url: self.registryURL, supportsAvailability: false) + configuration.scopedRegistries[scope] = registry } else { - configuration.defaultRegistry = .init(url: self.registryURL, supportsAvailability: false) + configuration.defaultRegistry = registry } } @@ -161,8 +165,8 @@ public struct SwiftPackageRegistryTool: AsyncParsableCommand { } extension URL { - func validateRegistryURL() throws { - guard self.scheme == "https" else { + func validateRegistryURL(allowHTTP: Bool = false) throws { + guard self.scheme == "https" || (self.scheme == "http" && allowHTTP) else { throw SwiftPackageRegistryTool.ValidationError.invalidURL(self) } } diff --git a/Tests/CommandsTests/PackageRegistryToolTests.swift b/Tests/CommandsTests/PackageRegistryToolTests.swift index fd0a33e23c9..31c803253e9 100644 --- a/Tests/CommandsTests/PackageRegistryToolTests.swift +++ b/Tests/CommandsTests/PackageRegistryToolTests.swift @@ -15,6 +15,7 @@ import Commands import Foundation import PackageLoading import PackageModel +import PackageRegistry @testable import PackageRegistryTool import PackageSigning import SPMTestSupport @@ -117,6 +118,19 @@ final class PackageRegistryToolTests: CommandsTestCase { XCTAssertEqual(json["version"], .int(1)) } + // Set default registry with allow-insecure-http option + do { + try execute(["set", "\(customRegistryBaseURL)", "--allow-insecure-http"], packagePath: packageRoot) + + let json = try JSON(data: localFileSystem.readFileContents(configurationFilePath)) + XCTAssertEqual(json["registries"]?.dictionary?.count, 1) + XCTAssertEqual( + json["registries"]?.dictionary?["[default]"]?.dictionary?["url"]?.string, + "\(customRegistryBaseURL)" + ) + XCTAssertEqual(json["version"], .int(1)) + } + // Unset default registry do { try execute(["unset"], packagePath: packageRoot) @@ -215,6 +229,40 @@ final class PackageRegistryToolTests: CommandsTestCase { } } + func testSetInsecureURL() throws { + try fixture(name: "DependencyResolution/External/Simple") { fixturePath in + let packageRoot = fixturePath.appending("Bar") + let configurationFilePath = AbsolutePath( + ".swiftpm/configuration/registries.json", + relativeTo: packageRoot + ) + + XCTAssertFalse(localFileSystem.exists(configurationFilePath)) + + // Set default registry + XCTAssertThrowsError(try execute(["set", "http://package.example.com"], packagePath: packageRoot)) + + XCTAssertFalse(localFileSystem.exists(configurationFilePath)) + } + } + + func testSetAllowedInsecureURL() throws { + try fixture(name: "DependencyResolution/External/Simple") { fixturePath in + let packageRoot = fixturePath.appending("Bar") + let configurationFilePath = AbsolutePath( + ".swiftpm/configuration/registries.json", + relativeTo: packageRoot + ) + + XCTAssertFalse(localFileSystem.exists(configurationFilePath)) + + // Set default registry + try execute(["set", "http://package.example.com", "--allow-insecure-http"], packagePath: packageRoot) + + XCTAssertTrue(localFileSystem.exists(configurationFilePath)) + } + } + func testSetInvalidScope() throws { try fixture(name: "DependencyResolution/External/Simple") { fixturePath in let packageRoot = fixturePath.appending("Bar") @@ -402,6 +450,133 @@ final class PackageRegistryToolTests: CommandsTestCase { } } + func testPublishingToHTTPRegistry() throws { + #if os(Linux) + // needed for archiving + guard SPM_posix_spawn_file_actions_addchdir_np_supported() else { + throw XCTSkip("working directory not supported on this platform") + } + #endif + + let packageIdentity = "test.my-package" + let version = "0.1.0" + let registryURL = "http://packages.example.com" + + _ = try withTemporaryDirectory { temporaryDirectory in + let packageDirectory = temporaryDirectory.appending("MyPackage") + try localFileSystem.createDirectory(packageDirectory) + + let initPackage = try InitPackage( + name: "MyPackage", + packageType: .executable, + destinationPath: packageDirectory, + fileSystem: localFileSystem + ) + try initPackage.writePackageStructure() + XCTAssertFileExists(packageDirectory.appending("Package.swift")) + + let workingDirectory = temporaryDirectory.appending(component: UUID().uuidString) + try localFileSystem.createDirectory(workingDirectory) + + XCTAssertThrowsError(try SwiftPM.Registry.execute( + [ + "publish", + packageIdentity, + version, + "--url=\(registryURL)", + "--scratch-directory=\(workingDirectory.pathString)", + "--package-path=\(packageDirectory.pathString)", + "--dry-run", + ] + )) + } + } + + func testPublishingToAllowedHTTPRegistry() throws { + #if os(Linux) + // needed for archiving + guard SPM_posix_spawn_file_actions_addchdir_np_supported() else { + throw XCTSkip("working directory not supported on this platform") + } + #endif + + let packageIdentity = "test.my-package" + let version = "0.1.0" + let registryURL = "http://packages.example.com" + + // with no authentication configured for registry + _ = try withTemporaryDirectory { temporaryDirectory in + let packageDirectory = temporaryDirectory.appending("MyPackage") + try localFileSystem.createDirectory(packageDirectory) + + let initPackage = try InitPackage( + name: "MyPackage", + packageType: .executable, + destinationPath: packageDirectory, + fileSystem: localFileSystem + ) + try initPackage.writePackageStructure() + XCTAssertFileExists(packageDirectory.appending("Package.swift")) + + let workingDirectory = temporaryDirectory.appending(component: UUID().uuidString) + try localFileSystem.createDirectory(workingDirectory) + + try SwiftPM.Registry.execute( + [ + "publish", + packageIdentity, + version, + "--url=\(registryURL)", + "--scratch-directory=\(workingDirectory.pathString)", + "--package-path=\(packageDirectory.pathString)", + "--allow-insecure-http", + "--dry-run", + ] + ) + } + + // with authentication configured for registry + _ = try withTemporaryDirectory { temporaryDirectory in + let packageDirectory = temporaryDirectory.appending("MyPackage") + try localFileSystem.createDirectory(packageDirectory) + + let initPackage = try InitPackage( + name: "MyPackage", + packageType: .executable, + destinationPath: packageDirectory, + fileSystem: localFileSystem + ) + try initPackage.writePackageStructure() + XCTAssertFileExists(packageDirectory.appending("Package.swift")) + + let workingDirectory = temporaryDirectory.appending(component: UUID().uuidString) + try localFileSystem.createDirectory(workingDirectory) + + let configurationFilePath = AbsolutePath( + ".swiftpm/configuration/registries.json", + relativeTo: packageDirectory + ) + + try localFileSystem.createDirectory(configurationFilePath.parentDirectory, recursive: true) + var configuration = RegistryConfiguration() + try configuration.add(authentication: .init(type: .basic), for: URL(registryURL)) + try localFileSystem.writeFileContents(configurationFilePath, data: JSONEncoder().encode(configuration)) + + XCTAssertThrowsError(try SwiftPM.Registry.execute( + [ + "publish", + packageIdentity, + version, + "--url=\(registryURL)", + "--scratch-directory=\(workingDirectory.pathString)", + "--package-path=\(packageDirectory.pathString)", + "--allow-insecure-http", + "--dry-run", + ] + )) + } + } + func testPublishingUnsignedPackage() throws { #if os(Linux) // needed for archiving @@ -903,13 +1078,18 @@ final class PackageRegistryToolTests: CommandsTestCase { } } + func testLoginRequiresHTTPS() { + let registryURL = URL(string: "http://packages.example.com")! + + XCTAssertThrowsError(try SwiftPM.Registry.execute(["login", "--url", registryURL.absoluteString])) + } + func testCreateLoginURL() { let registryURL = URL(string: "https://packages.example.com")! XCTAssertEqual(try SwiftPackageRegistryTool.Login.loginURL(from: registryURL, loginAPIPath: nil).absoluteString, "https://packages.example.com/login") XCTAssertEqual(try SwiftPackageRegistryTool.Login.loginURL(from: registryURL, loginAPIPath: "/secret-sign-in").absoluteString, "https://packages.example.com/secret-sign-in") - } func testCreateLoginURLMaintainsPort() { @@ -920,6 +1100,18 @@ final class PackageRegistryToolTests: CommandsTestCase { XCTAssertEqual(try SwiftPackageRegistryTool.Login.loginURL(from: registryURL, loginAPIPath: "/secret-sign-in").absoluteString, "https://packages.example.com:8081/secret-sign-in") } + func testValidateRegistryURL() throws { + // Valid + try URL(string: "https://packages.example.com")!.validateRegistryURL() + try URL(string: "http://packages.example.com")!.validateRegistryURL(allowHTTP: true) + + // Invalid + XCTAssertThrowsError(try URL(string: "http://packages.example.com")!.validateRegistryURL()) + XCTAssertThrowsError(try URL(string: "http://packages.example.com")!.validateRegistryURL(allowHTTP: false)) + XCTAssertThrowsError(try URL(string: "ssh://packages.example.com")!.validateRegistryURL()) + XCTAssertThrowsError(try URL(string: "ftp://packages.example.com")!.validateRegistryURL(allowHTTP: true)) + } + private func testRoots() throws -> [[UInt8]] { try fixture(name: "Signing", createGitRepo: false) { fixturePath in let rootCA = try localFileSystem diff --git a/Tests/PackageRegistryTests/RegistryConfigurationTests.swift b/Tests/PackageRegistryTests/RegistryConfigurationTests.swift index d7fbed94526..93375acf908 100644 --- a/Tests/PackageRegistryTests/RegistryConfigurationTests.swift +++ b/Tests/PackageRegistryTests/RegistryConfigurationTests.swift @@ -138,7 +138,7 @@ final class RegistryConfigurationTests: XCTestCase { }, "bar": { "url": "\#(customRegistryBaseURL)" - }, + } }, "authentication": { "packages.example.com": { From 414fed6846cfcad38ecb0b704021705f162550a6 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Fri, 16 Feb 2024 22:43:38 +0900 Subject: [PATCH 007/159] Fix test runner build on WASI (#7341) ### Motivation: The test runner build was failing on WASI after the introduction of the experimental summary output. ### Modifications: This PR fixes several minor issues to pass `--build-tests` build on WASI: * Missing `WASILibc` import * The use of `flock` which is not available on WASI. * Signature incompatibility of `XCTMain` on WASI. ### Result: `swift build --build-tests --triple wasm32-unknown-wasi` will pass when compiler supports the target. --- .../Build/BuildOperationBuildSystemDelegateHandler.swift | 7 +++++++ Sources/Build/TestObservation.swift | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/Sources/Build/BuildOperationBuildSystemDelegateHandler.swift b/Sources/Build/BuildOperationBuildSystemDelegateHandler.swift index 47adeef8179..77d1b3cf5b8 100644 --- a/Sources/Build/BuildOperationBuildSystemDelegateHandler.swift +++ b/Sources/Build/BuildOperationBuildSystemDelegateHandler.swift @@ -265,7 +265,14 @@ final class TestEntryPointCommand: CustomLLBuildCommand, TestBuildCommand { struct Runner { static func main() { \#(testObservabilitySetup) + #if os(WASI) + // FIXME: On WASI, XCTest uses `Task` based waiting not to block the whole process, so + // the `XCTMain` call can return the control and the process will exit by `exit(0)` later. + // This is a workaround until we have WASI threads or swift-testing, which does not block threads. + XCTMain(__allDiscoveredTests()) + #else XCTMain(__allDiscoveredTests()) as Never + #endif } } """# diff --git a/Sources/Build/TestObservation.swift b/Sources/Build/TestObservation.swift index 49e4e7cbe30..98a8f5f8e1e 100644 --- a/Sources/Build/TestObservation.swift +++ b/Sources/Build/TestObservation.swift @@ -130,6 +130,8 @@ public func generateTestObservationCode(buildParameters: BuildParameters) -> Str #elseif os(Windows) @_exported import CRT @_exported import WinSDK + #elseif os(WASI) + @_exported import WASILibc #else @_exported import Darwin.C #endif @@ -176,6 +178,8 @@ public func generateTestObservationCode(buildParameters: BuildParameters) -> Str UInt32.max, UInt32.max, &overlapped) { throw ProcessLockError.unableToAquireLock(errno: Int32(GetLastError())) } + #elseif os(WASI) + // WASI doesn't support flock #else if fileDescriptor == nil { let fd = open(lockFile.path, O_WRONLY | O_CREAT | O_CLOEXEC, 0o666) @@ -201,6 +205,8 @@ public func generateTestObservationCode(buildParameters: BuildParameters) -> Str overlapped.OffsetHigh = 0 overlapped.hEvent = nil UnlockFileEx(handle, 0, UInt32.max, UInt32.max, &overlapped) + #elseif os(WASI) + // WASI doesn't support flock #else guard let fd = fileDescriptor else { return } flock(fd, LOCK_UN) @@ -211,6 +217,8 @@ public func generateTestObservationCode(buildParameters: BuildParameters) -> Str #if os(Windows) guard let handle = handle else { return } CloseHandle(handle) + #elseif os(WASI) + // WASI doesn't support flock #else guard let fd = fileDescriptor else { return } close(fd) From cae580e9a2c7d32ece1ad08c7e802c3bc27dc32a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Boris=20B=C3=BCgling?= Date: Fri, 16 Feb 2024 12:21:50 -0800 Subject: [PATCH 008/159] Support for prebuilt packages in the SDK (#7337) This should allow satisfying a package dependency with a package in the SDK (or toolchain) based on metadata. If the given prebuilt library is version-compatible, we essentially elide the dependency from the graph. If it is not, we will fetch and build the dependency as normal. If a library with associated metadata is linked without a corresponding package dependency, we'll emit a warning. --- Package.swift | 5 +- Sources/Build/BuildOperation.swift | 87 +++++++++++++++ Sources/CMakeLists.txt | 1 + .../Commands/PackageTools/EditCommands.swift | 1 + Sources/CoreCommands/BuildSystemSupport.swift | 3 + Sources/CoreCommands/SwiftTool.swift | 26 ++++- Sources/PackageGraph/CMakeLists.txt | 1 + .../PackageGraph/PackageGraph+Loading.swift | 33 ++++-- .../PrebuiltPackageContainer.swift | 69 ++++++++++++ .../PubGrub/ContainerProvider.swift | 27 ++++- .../PubGrub/PubGrubDependencyResolver.swift | 103 +++++++++++++++--- Sources/PackageModel/CMakeLists.txt | 1 + .../LibraryMetadata.swift | 54 +++++++++ .../provided-libraries.json | 16 +++ Sources/PackageModel/Toolchain.swift | 3 + Sources/PackageModel/UserToolchain.swift | 32 +++++- .../SPMTestSupport/MockBuildTestHelper.swift | 1 + Sources/SPMTestSupport/MockWorkspace.swift | 23 +++- Sources/SPMTestSupport/Resolver.swift | 19 ++++ Sources/SPMTestSupport/misc.swift | 1 + .../ResolverPrecomputationProvider.swift | 16 ++- .../Workspace/Workspace+Dependencies.swift | 60 +++++++++- Sources/Workspace/Workspace+Editing.swift | 3 + Sources/Workspace/Workspace+Manifests.swift | 73 ++++++++++--- Sources/Workspace/Workspace.swift | 15 +++ Sources/swift-bootstrap/main.swift | 3 + Tests/BuildTests/BuildOperationTests.swift | 63 +++++++++++ Tests/CommandsTests/PackageToolTests.swift | 6 +- Tests/FunctionalTests/PluginTests.swift | 24 +++- .../PackageGraphPerfTests.swift | 1 + Tests/PackageGraphTests/PubgrubTests.swift | 41 +++++++ .../PluginInvocationTests.swift | 35 +++++- 32 files changed, 772 insertions(+), 74 deletions(-) create mode 100644 Sources/PackageGraph/PrebuiltPackageContainer.swift create mode 100644 Sources/PackageModel/InstalledLibrariesSupport/LibraryMetadata.swift create mode 100644 Sources/PackageModel/InstalledLibrariesSupport/provided-libraries.json create mode 100644 Sources/SPMTestSupport/Resolver.swift create mode 100644 Tests/BuildTests/BuildOperationTests.swift diff --git a/Package.swift b/Package.swift index 4cb31ec61bf..40d4b5f6ae9 100644 --- a/Package.swift +++ b/Package.swift @@ -217,7 +217,10 @@ let package = Package( /** Primitive Package model objects */ name: "PackageModel", dependencies: ["Basics"], - exclude: ["CMakeLists.txt", "README.md"] + exclude: ["CMakeLists.txt", "README.md"], + resources: [ + .copy("InstalledLibrariesSupport/provided-libraries.json"), + ] ), .target( diff --git a/Sources/Build/BuildOperation.swift b/Sources/Build/BuildOperation.swift index 2342f8ddc47..fab2429cccf 100644 --- a/Sources/Build/BuildOperation.swift +++ b/Sources/Build/BuildOperation.swift @@ -102,6 +102,12 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS /// Alternative path to search for pkg-config `.pc` files. private let pkgConfigDirectories: [AbsolutePath] + /// Map of dependency package identities by root packages that depend on them. + private let dependenciesByRootPackageIdentity: [PackageIdentity: [PackageIdentity]] + + /// Map of root package identities by target names which are declared in them. + private let rootPackageIdentityByTargetName: [String: PackageIdentity] + public init( productsBuildParameters: BuildParameters, toolsBuildParameters: BuildParameters, @@ -110,6 +116,8 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS pluginConfiguration: PluginConfiguration? = .none, additionalFileRules: [FileRuleDescription], pkgConfigDirectories: [AbsolutePath], + dependenciesByRootPackageIdentity: [PackageIdentity: [PackageIdentity]], + targetsByRootPackageIdentity: [PackageIdentity: [String]], outputStream: OutputByteStream, logLevel: Basics.Diagnostic.Severity, fileSystem: Basics.FileSystem, @@ -129,6 +137,8 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS self.additionalFileRules = additionalFileRules self.pluginConfiguration = pluginConfiguration self.pkgConfigDirectories = pkgConfigDirectories + self.dependenciesByRootPackageIdentity = dependenciesByRootPackageIdentity + self.rootPackageIdentityByTargetName = (try? Dictionary(throwingUniqueKeysWithValues: targetsByRootPackageIdentity.lazy.flatMap { e in e.value.map { ($0, e.key) } })) ?? [:] self.outputStream = outputStream self.logLevel = logLevel self.fileSystem = fileSystem @@ -248,6 +258,79 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS } } + private static var didEmitUnexpressedDependencies = false + + private func detectUnexpressedDependencies() { + return self.detectUnexpressedDependencies( + // Note: once we switch from the toolchain global metadata, we will have to ensure we can match the right metadata used during the build. + availableLibraries: self.productsBuildParameters.toolchain.providedLibraries, + targetDependencyMap: self.buildDescription.targetDependencyMap + ) + } + + // TODO: Currently this function will only match frameworks. + internal func detectUnexpressedDependencies( + availableLibraries: [LibraryMetadata], + targetDependencyMap: [String: [String]]? + ) { + // Ensure we only emit these once, regardless of how many builds are being done. + guard !Self.didEmitUnexpressedDependencies else { + return + } + Self.didEmitUnexpressedDependencies = true + + let availableFrameworks = Dictionary(uniqueKeysWithValues: availableLibraries.compactMap { + if let identity = Set($0.identities.map(\.identity)).spm_only { + return ("\($0.productName!).framework", identity) + } else { + return nil + } + }) + + targetDependencyMap?.keys.forEach { targetName in + let c99name = targetName.spm_mangledToC99ExtendedIdentifier() + // Since we're analysing post-facto, we don't know which parameters are the correct ones. + let possibleTempsPaths = [productsBuildParameters, toolsBuildParameters].map { + $0.buildPath.appending(component: "\(c99name).build") + } + + let usedSDKDependencies: [String] = Set(possibleTempsPaths).flatMap { possibleTempsPath in + guard let contents = try? self.fileSystem.readFileContents(possibleTempsPath.appending(component: "\(c99name).d")) else { + return [String]() + } + + // FIXME: We need a real makefile deps parser here... + let deps = contents.description.split(whereSeparator: { $0.isWhitespace }) + return deps.filter { + !$0.hasPrefix(possibleTempsPath.parentDirectory.pathString) + }.compactMap { + try? AbsolutePath(validating: String($0)) + }.compactMap { + return $0.components.first(where: { $0.hasSuffix(".framework") }) + } + } + + let dependencies: [PackageIdentity] + if let rootPackageIdentity = self.rootPackageIdentityByTargetName[targetName] { + dependencies = self.dependenciesByRootPackageIdentity[rootPackageIdentity] ?? [] + } else { + dependencies = [] + } + + Set(usedSDKDependencies).forEach { + if availableFrameworks.keys.contains($0) { + if let availableFrameworkPackageIdentity = availableFrameworks[$0], !dependencies.contains( + availableFrameworkPackageIdentity + ) { + observabilityScope.emit( + warning: "target '\(targetName)' has an unexpressed depedency on '\(availableFrameworkPackageIdentity)'" + ) + } + } + } + } + } + /// Perform a build using the given build description and subset. public func build(subset: BuildSubset) throws { guard !self.productsBuildParameters.shouldSkipBuilding else { @@ -284,6 +367,8 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS let duration = buildStartTime.distance(to: .now()) + self.detectUnexpressedDependencies() + let subsetDescriptor: String? switch subset { case .product(let productName): @@ -466,6 +551,8 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS packageGraphLoader: { return graph }, additionalFileRules: self.additionalFileRules, pkgConfigDirectories: self.pkgConfigDirectories, + dependenciesByRootPackageIdentity: [:], + targetsByRootPackageIdentity: [:], outputStream: self.outputStream, logLevel: self.logLevel, fileSystem: self.fileSystem, diff --git a/Sources/CMakeLists.txt b/Sources/CMakeLists.txt index da35c85d157..a8aa9f771bc 100644 --- a/Sources/CMakeLists.txt +++ b/Sources/CMakeLists.txt @@ -6,6 +6,7 @@ # See http://swift.org/LICENSE.txt for license information # See http://swift.org/CONTRIBUTORS.txt for Swift project authors +add_compile_definitions(SKIP_RESOURCE_SUPPORT) add_compile_definitions(USE_IMPL_ONLY_IMPORTS) add_subdirectory(SPMSQLite3) diff --git a/Sources/Commands/PackageTools/EditCommands.swift b/Sources/Commands/PackageTools/EditCommands.swift index c4249d974eb..555c3a5abb8 100644 --- a/Sources/Commands/PackageTools/EditCommands.swift +++ b/Sources/Commands/PackageTools/EditCommands.swift @@ -72,6 +72,7 @@ extension SwiftPackageTool { packageName: packageName, forceRemove: shouldForceRemove, root: swiftTool.getWorkspaceRoot(), + availableLibraries: swiftTool.getHostToolchain().providedLibraries, observabilityScope: swiftTool.observabilityScope ) } diff --git a/Sources/CoreCommands/BuildSystemSupport.swift b/Sources/CoreCommands/BuildSystemSupport.swift index dcbc8aa6bbc..1f2a45bbdc7 100644 --- a/Sources/CoreCommands/BuildSystemSupport.swift +++ b/Sources/CoreCommands/BuildSystemSupport.swift @@ -32,6 +32,7 @@ private struct NativeBuildSystemFactory: BuildSystemFactory { logLevel: Diagnostic.Severity?, observabilityScope: ObservabilityScope? ) throws -> any BuildSystem { + let rootPackageInfo = try swiftTool.getRootPackageInformation() let testEntryPointPath = productsBuildParameters?.testingParameters.testProductStyle.explicitlySpecifiedEntryPointPath return try BuildOperation( productsBuildParameters: try productsBuildParameters ?? self.swiftTool.productsBuildParameters, @@ -50,6 +51,8 @@ private struct NativeBuildSystemFactory: BuildSystemFactory { ), additionalFileRules: FileRuleDescription.swiftpmFileTypes, pkgConfigDirectories: self.swiftTool.options.locations.pkgConfigDirectories, + dependenciesByRootPackageIdentity: rootPackageInfo.dependecies, + targetsByRootPackageIdentity: rootPackageInfo.targets, outputStream: outputStream ?? self.swiftTool.outputStream, logLevel: logLevel ?? self.swiftTool.logLevel, fileSystem: self.swiftTool.fileSystem, diff --git a/Sources/CoreCommands/SwiftTool.swift b/Sources/CoreCommands/SwiftTool.swift index a23c80ff008..6126f8ca286 100644 --- a/Sources/CoreCommands/SwiftTool.swift +++ b/Sources/CoreCommands/SwiftTool.swift @@ -48,7 +48,7 @@ import var TSCBasic.stderrStream import class TSCBasic.TerminalController import class TSCBasic.ThreadSafeOutputByteStream -import var TSCUtility.verbosity +import TSCUtility // cannot be scoped because of `String.spm_mangleToC99ExtendedIdentifier()` typealias Diagnostic = Basics.Diagnostic @@ -460,6 +460,29 @@ public final class SwiftTool { return workspace } + public func getRootPackageInformation() throws -> (dependecies: [PackageIdentity: [PackageIdentity]], targets: [PackageIdentity: [String]]) { + let workspace = try self.getActiveWorkspace() + let root = try self.getWorkspaceRoot() + let rootManifests = try temp_await { + workspace.loadRootManifests( + packages: root.packages, + observabilityScope: self.observabilityScope, + completion: $0 + ) + } + + var identities = [PackageIdentity: [PackageIdentity]]() + var targets = [PackageIdentity: [String]]() + + rootManifests.forEach { + let identity = PackageIdentity(path: $0.key) + identities[identity] = $0.value.dependencies.map(\.identity) + targets[identity] = $0.value.targets.map { $0.name.spm_mangledToC99ExtendedIdentifier() } + } + + return (identities, targets) + } + private func getEditsDirectory() throws -> AbsolutePath { // TODO: replace multiroot-data-file with explicit overrides if let multiRootPackageDataFile = options.locations.multirootPackageDataFile { @@ -581,6 +604,7 @@ public final class SwiftTool { explicitProduct: explicitProduct, forceResolvedVersions: options.resolver.forceResolvedVersions, testEntryPointPath: testEntryPointPath, + availableLibraries: self.getHostToolchain().providedLibraries, observabilityScope: self.observabilityScope ) diff --git a/Sources/PackageGraph/CMakeLists.txt b/Sources/PackageGraph/CMakeLists.txt index c1742e54c44..9d6e1f5bcae 100644 --- a/Sources/PackageGraph/CMakeLists.txt +++ b/Sources/PackageGraph/CMakeLists.txt @@ -19,6 +19,7 @@ add_library(PackageGraph PackageGraphRoot.swift PackageModel+Extensions.swift PackageRequirement.swift + PrebuiltPackageContainer.swift PinsStore.swift Resolution/PubGrub/Assignment.swift Resolution/PubGrub/ContainerProvider.swift diff --git a/Sources/PackageGraph/PackageGraph+Loading.swift b/Sources/PackageGraph/PackageGraph+Loading.swift index 7627e0d5439..420e1ccce67 100644 --- a/Sources/PackageGraph/PackageGraph+Loading.swift +++ b/Sources/PackageGraph/PackageGraph+Loading.swift @@ -32,6 +32,7 @@ extension PackageGraph { customPlatformsRegistry: PlatformRegistry? = .none, customXCTestMinimumDeploymentTargets: [PackageModel.Platform: PlatformVersion]? = .none, testEntryPointPath: AbsolutePath? = nil, + availableLibraries: [LibraryMetadata], fileSystem: FileSystem, observabilityScope: ObservabilityScope ) throws -> PackageGraph { @@ -159,6 +160,7 @@ extension PackageGraph { unsafeAllowedPackages: unsafeAllowedPackages, platformRegistry: customPlatformsRegistry ?? .default, platformVersionProvider: platformVersionProvider, + availableLibraries: availableLibraries, fileSystem: fileSystem, observabilityScope: observabilityScope ) @@ -243,6 +245,7 @@ private func createResolvedPackages( unsafeAllowedPackages: Set, platformRegistry: PlatformRegistry, platformVersionProvider: PlatformVersionProvider, + availableLibraries: [LibraryMetadata], fileSystem: FileSystem, observabilityScope: ObservabilityScope ) throws -> [ResolvedPackage] { @@ -513,18 +516,24 @@ private func createResolvedPackages( }.map {$0.targets}.flatMap{$0}.filter { t in t.name != productRef.name } - - // Find a product name from the available product dependencies that is most similar to the required product name. - let bestMatchedProductName = bestMatch(for: productRef.name, from: Array(allTargetNames)) - let error = PackageGraphError.productDependencyNotFound( - package: package.identity.description, - targetName: targetBuilder.target.name, - dependencyProductName: productRef.name, - dependencyPackageName: productRef.package, - dependencyProductInDecl: !declProductsAsDependency.isEmpty, - similarProductName: bestMatchedProductName - ) - packageObservabilityScope.emit(error) + + let identitiesAvailableInSDK = availableLibraries.flatMap { $0.identities.map { $0.identity } } + // TODO: Do we have to care about "name" vs. identity here? + if let name = productRef.package, identitiesAvailableInSDK.contains(PackageIdentity.plain(name)) { + // Do not emit any diagnostic. + } else { + // Find a product name from the available product dependencies that is most similar to the required product name. + let bestMatchedProductName = bestMatch(for: productRef.name, from: Array(allTargetNames)) + let error = PackageGraphError.productDependencyNotFound( + package: package.identity.description, + targetName: targetBuilder.target.name, + dependencyProductName: productRef.name, + dependencyPackageName: productRef.package, + dependencyProductInDecl: !declProductsAsDependency.isEmpty, + similarProductName: bestMatchedProductName + ) + packageObservabilityScope.emit(error) + } } continue } diff --git a/Sources/PackageGraph/PrebuiltPackageContainer.swift b/Sources/PackageGraph/PrebuiltPackageContainer.swift new file mode 100644 index 00000000000..870487d1781 --- /dev/null +++ b/Sources/PackageGraph/PrebuiltPackageContainer.swift @@ -0,0 +1,69 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Basics +import PackageModel +import struct TSCUtility.Version + +/// A package container that can represent a prebuilt library from a package. +public struct PrebuiltPackageContainer: PackageContainer { + private let chosenIdentity: LibraryMetadata.Identity + private let metadata: LibraryMetadata + + public init(metadata: LibraryMetadata) throws { + self.metadata = metadata + + // FIXME: Unclear what is supposed to happen if we have multiple identities. + if let identity = metadata.identities.first { + self.chosenIdentity = identity + } else { + let name = metadata.productName.map { "'\($0)' " } ?? "" + throw InternalError("provided library \(name)does not specifiy any identities") + } + } + + public var package: PackageReference { + return .init(identity: chosenIdentity.identity, kind: chosenIdentity.kind) + } + + public func isToolsVersionCompatible(at version: Version) -> Bool { + return true + } + + public func toolsVersion(for version: Version) throws -> ToolsVersion { + return .v4 + } + + public func toolsVersionsAppropriateVersionsDescending() throws -> [Version] { + return try versionsAscending() + } + + public func versionsAscending() throws -> [Version] { + return [.init(stringLiteral: metadata.version)] + } + + public func getDependencies(at version: Version, productFilter: ProductFilter) throws -> [PackageContainerConstraint] { + return [] + } + + public func getDependencies(at revision: String, productFilter: ProductFilter) throws -> [PackageContainerConstraint] { + return [] + } + + public func getUnversionedDependencies(productFilter: ProductFilter) throws -> [PackageContainerConstraint] { + return [] + } + + public func loadPackageReference(at boundVersion: BoundVersion) throws -> PackageReference { + return package + } +} diff --git a/Sources/PackageGraph/Resolution/PubGrub/ContainerProvider.swift b/Sources/PackageGraph/Resolution/PubGrub/ContainerProvider.swift index f952a066a99..74735c9ccf9 100644 --- a/Sources/PackageGraph/Resolution/PubGrub/ContainerProvider.swift +++ b/Sources/PackageGraph/Resolution/PubGrub/ContainerProvider.swift @@ -48,6 +48,12 @@ final class ContainerProvider { self.observabilityScope = observabilityScope } + func removeCachedContainers(for packages: [PackageReference]) { + packages.forEach { + self.containersCache[$0] = nil + } + } + /// Get a cached container for the given identifier, asserting / throwing if not found. func getCachedContainer(for package: PackageReference) throws -> PubGrubPackageContainer { guard let container = self.containersCache[package] else { @@ -59,6 +65,7 @@ final class ContainerProvider { /// Get the container for the given identifier, loading it if necessary. func getContainer( for package: PackageReference, + availableLibraries: [LibraryMetadata], completion: @escaping (Result) -> Void ) { // Return the cached container, if available. @@ -66,6 +73,17 @@ final class ContainerProvider { return completion(.success(container)) } + if let metadata = package.matchingPrebuiltLibrary(in: availableLibraries) { + do { + let prebuiltPackageContainer = try PrebuiltPackageContainer(metadata: metadata) + let pubGrubContainer = PubGrubPackageContainer(underlying: prebuiltPackageContainer, pins: self.pins) + self.containersCache[package] = pubGrubContainer + return completion(.success(pubGrubContainer)) + } catch { + return completion(.failure(error)) + } + } + if let prefetchSync = self.prefetches[package] { // If this container is already being prefetched, wait for that to complete prefetchSync.notify(queue: .sharedConcurrent) { @@ -75,7 +93,7 @@ final class ContainerProvider { } else { // if prefetch failed, remove from list of prefetches and try again self.prefetches[package] = nil - return self.getContainer(for: package, completion: completion) + return self.getContainer(for: package, availableLibraries: availableLibraries, completion: completion) } } } else { @@ -98,9 +116,12 @@ final class ContainerProvider { } /// Starts prefetching the given containers. - func prefetch(containers identifiers: [PackageReference]) { + func prefetch(containers identifiers: [PackageReference], availableLibraries: [LibraryMetadata]) { + let filteredIdentifiers = identifiers.filter { + return $0.matchingPrebuiltLibrary(in: availableLibraries) == nil + } // Process each container. - for identifier in identifiers { + for identifier in filteredIdentifiers { var needsFetching = false self.prefetches.memoize(identifier) { let group = DispatchGroup() diff --git a/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift b/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift index 6c02e492d0e..2d1993bbcbe 100644 --- a/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift +++ b/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift @@ -140,7 +140,11 @@ public struct PubGrubDependencyResolver { } /// Execute the resolution algorithm to find a valid assignment of versions. - public func solve(constraints: [Constraint]) -> Result<[DependencyResolverBinding], Error> { + public func solve(constraints: [Constraint], availableLibraries: [LibraryMetadata], preferPrebuiltLibraries: Bool) -> Result<[DependencyResolverBinding], Error> { + if !preferPrebuiltLibraries { + self.provider.removeCachedContainers(for: availableLibraries.flatMap { $0.identities.map { $0.ref } }) + } + // the graph resolution root let root: DependencyResolutionNode if constraints.count == 1, let constraint = constraints.first, constraint.package.kind.isRoot { @@ -155,11 +159,26 @@ public struct PubGrubDependencyResolver { } do { + // Use empty `availableLibraries` for the rest of resolving if we don't prefer them. + let availableLibraries = preferPrebuiltLibraries ? availableLibraries : [] // strips state - return .success(try self.solve(root: root, constraints: constraints).bindings) + let bindings = try self.solve(root: root, constraints: constraints, availableLibraries: availableLibraries).bindings.filter { + return $0.package.matchingPrebuiltLibrary(in: availableLibraries) == nil + } + return .success(bindings) } catch { // If version solving failing, build the user-facing diagnostic. if let pubGrubError = error as? PubgrubError, let rootCause = pubGrubError.rootCause, let incompatibilities = pubGrubError.incompatibilities { + let incompatiblePackages = incompatibilities.map({ $0.key.package }) + if incompatiblePackages.contains(where: { $0.matchingPrebuiltLibrary(in: availableLibraries) != nil }) { + return .failure( + PubgrubError.potentiallyUnresovableDueToPrebuiltLibrary( + incompatiblePackages, + pubGrubError.description + ) + ) + } + do { var builder = DiagnosticReportBuilder( root: root, @@ -180,9 +199,12 @@ public struct PubGrubDependencyResolver { /// Find a set of dependencies that fit the given constraints. If dependency /// resolution is unable to provide a result, an error is thrown. /// - Warning: It is expected that the root package reference has been set before this is called. - internal func solve(root: DependencyResolutionNode, constraints: [Constraint]) throws -> (bindings: [DependencyResolverBinding], state: State) { + internal func solve(root: DependencyResolutionNode, constraints: [Constraint], availableLibraries: [LibraryMetadata] = []) throws -> ( + bindings: [DependencyResolverBinding], + state: State + ) { // first process inputs - let inputs = try self.processInputs(root: root, with: constraints) + let inputs = try self.processInputs(root: root, with: constraints, availableLibraries: availableLibraries) // Prefetch the containers if prefetching is enabled. if self.prefetchBasedOnResolvedFile { @@ -192,7 +214,7 @@ public struct PubGrubDependencyResolver { let pins = self.pins.values .map(\.packageRef) .filter { !inputs.overriddenPackages.keys.contains($0) } - self.provider.prefetch(containers: pins) + self.provider.prefetch(containers: pins, availableLibraries: availableLibraries) } let state = State(root: root, overriddenPackages: inputs.overriddenPackages) @@ -208,7 +230,7 @@ public struct PubGrubDependencyResolver { state.addIncompatibility(incompatibility, at: .topLevel) } - try self.run(state: state) + try self.run(state: state, availableLibraries: availableLibraries) let decisions = state.solution.assignments.filter(\.isDecision) var flattenedAssignments: [PackageReference: (binding: BoundVersion, products: ProductFilter)] = [:] @@ -228,7 +250,7 @@ public struct PubGrubDependencyResolver { let products = assignment.term.node.productFilter // TODO: replace with async/await when available - let container = try temp_await { provider.getContainer(for: assignment.term.node.package, completion: $0) } + let container = try temp_await { provider.getContainer(for: assignment.term.node.package, availableLibraries: availableLibraries, completion: $0) } let updatePackage = try container.underlying.loadPackageReference(at: boundVersion) if var existing = flattenedAssignments[updatePackage] { @@ -250,7 +272,7 @@ public struct PubGrubDependencyResolver { // Add overridden packages to the result. for (package, override) in state.overriddenPackages { // TODO: replace with async/await when available - let container = try temp_await { provider.getContainer(for: package, completion: $0) } + let container = try temp_await { provider.getContainer(for: package, availableLibraries: availableLibraries, completion: $0) } let updatePackage = try container.underlying.loadPackageReference(at: override.version) finalAssignments.append(.init( package: updatePackage, @@ -266,7 +288,8 @@ public struct PubGrubDependencyResolver { private func processInputs( root: DependencyResolutionNode, - with constraints: [Constraint] + with constraints: [Constraint], + availableLibraries: [LibraryMetadata] ) throws -> ( overriddenPackages: [PackageReference: (version: BoundVersion, products: ProductFilter)], rootIncompatibilities: [Incompatibility] @@ -311,7 +334,7 @@ public struct PubGrubDependencyResolver { // be process at the end. This allows us to override them when there is a non-version // based (unversioned/branch-based) constraint present in the graph. // TODO: replace with async/await when available - let container = try temp_await { provider.getContainer(for: node.package, completion: $0) } + let container = try temp_await { provider.getContainer(for: node.package, availableLibraries: availableLibraries, completion: $0) } for dependency in try container.underlying.getUnversionedDependencies(productFilter: node.productFilter) { if let versionedBasedConstraints = VersionBasedConstraint.constraints(dependency) { for constraint in versionedBasedConstraints { @@ -359,7 +382,7 @@ public struct PubGrubDependencyResolver { // Process dependencies of this package, similar to the first phase but branch-based dependencies // are not allowed to contain local/unversioned packages. // TODO: replace with async/await when avail - let container = try temp_await { provider.getContainer(for: package, completion: $0) } + let container = try temp_await { provider.getContainer(for: package, availableLibraries: availableLibraries, completion: $0) } // If there is a pin for this revision-based dependency, get // the dependencies at the pinned revision instead of using @@ -442,19 +465,22 @@ public struct PubGrubDependencyResolver { /// decisions if nothing else is left to be done. /// After this method returns `solution` is either populated with a list of /// final version assignments or an error is thrown. - private func run(state: State) throws { + private func run(state: State, availableLibraries: [LibraryMetadata]) throws { var next: DependencyResolutionNode? = state.root while let nxt = next { try self.propagate(state: state, node: nxt) // initiate prefetch of known packages that will be used to make the decision on the next step - self.provider.prefetch(containers: state.solution.undecided.map(\.node.package)) + self.provider.prefetch( + containers: state.solution.undecided.map(\.node.package), + availableLibraries: availableLibraries + ) // If decision making determines that no more decisions are to be // made, it returns nil to signal that version solving is done. // TODO: replace with async/await when available - next = try temp_await { self.makeDecision(state: state, completion: $0) } + next = try temp_await { self.makeDecision(state: state, availableLibraries: availableLibraries, completion: $0) } } } @@ -630,7 +656,11 @@ public struct PubGrubDependencyResolver { incompatibility.terms.isEmpty || (incompatibility.terms.count == 1 && incompatibility.terms.first?.node == root) } - private func computeCounts(for terms: [Term], completion: @escaping (Result<[Term: Int], Error>) -> Void) { + private func computeCounts( + for terms: [Term], + availableLibraries: [LibraryMetadata], + completion: @escaping (Result<[Term: Int], Error>) -> Void + ) { if terms.isEmpty { return completion(.success([:])) } @@ -640,7 +670,7 @@ public struct PubGrubDependencyResolver { terms.forEach { term in sync.enter() - provider.getContainer(for: term.node.package) { result in + provider.getContainer(for: term.node.package, availableLibraries: availableLibraries) { result in defer { sync.leave() } results[term] = result.flatMap { container in Result(catching: { try container.versionCount(term.requirement) }) } } @@ -655,7 +685,11 @@ public struct PubGrubDependencyResolver { } } - internal func makeDecision(state: State, completion: @escaping (Result) -> Void) { + internal func makeDecision( + state: State, + availableLibraries: [LibraryMetadata] = [], + completion: @escaping (Result) -> Void + ) { // If there are no more undecided terms, version solving is complete. let undecided = state.solution.undecided guard !undecided.isEmpty else { @@ -664,7 +698,7 @@ public struct PubGrubDependencyResolver { // Prefer packages with least number of versions that fit the current requirements so we // get conflicts (if any) sooner. - self.computeCounts(for: undecided) { result in + self.computeCounts(for: undecided, availableLibraries: availableLibraries) { result in do { let start = DispatchTime.now() let counts = try result.get() @@ -728,6 +762,7 @@ public extension PubGrubDependencyResolver { enum PubgrubError: Swift.Error, CustomStringConvertible { case _unresolvable(Incompatibility, [DependencyResolutionNode: [Incompatibility]]) case unresolvable(String) + case potentiallyUnresovableDueToPrebuiltLibrary([PackageReference], String) public var description: String { switch self { @@ -735,6 +770,8 @@ public extension PubGrubDependencyResolver { return rootCause.description case .unresolvable(let error): return error + case .potentiallyUnresovableDueToPrebuiltLibrary(_, let error): + return error } } @@ -744,6 +781,8 @@ public extension PubGrubDependencyResolver { return rootCause case .unresolvable: return nil + case .potentiallyUnresovableDueToPrebuiltLibrary: + return nil } } @@ -753,6 +792,8 @@ public extension PubGrubDependencyResolver { return incompatibilities case .unresolvable: return nil + case .potentiallyUnresovableDueToPrebuiltLibrary: + return nil } } } @@ -797,3 +838,29 @@ private extension PackageRequirement { } } } + +extension PackageReference { + public func matchingPrebuiltLibrary(in availableLibraries: [LibraryMetadata]) -> LibraryMetadata? { + switch self.kind { + case .fileSystem, .localSourceControl, .root: + return nil // can never match a prebuilt library + case .registry(let identity): + if let registryIdentity = identity.registry { + return availableLibraries.first( + where: { $0.identities.contains( + where: { $0 == .packageIdentity( + scope: registryIdentity.scope.description, + name: registryIdentity.name.description + ) + }) + }) + } else { + return nil + } + case .remoteSourceControl(let url): + return availableLibraries.first(where: { + $0.identities.contains(where: { $0 == .sourceControl(url: url) }) + }) + } + } +} diff --git a/Sources/PackageModel/CMakeLists.txt b/Sources/PackageModel/CMakeLists.txt index 4f9fce12ef4..26a19962746 100644 --- a/Sources/PackageModel/CMakeLists.txt +++ b/Sources/PackageModel/CMakeLists.txt @@ -15,6 +15,7 @@ add_library(PackageModel DependencyMapper.swift Diagnostics.swift IdentityResolver.swift + InstalledLibrariesSupport/LibraryMetadata.swift InstalledSwiftPMConfiguration.swift Manifest/Manifest.swift Manifest/PackageConditionDescription.swift diff --git a/Sources/PackageModel/InstalledLibrariesSupport/LibraryMetadata.swift b/Sources/PackageModel/InstalledLibrariesSupport/LibraryMetadata.swift new file mode 100644 index 00000000000..7e12d31a6f3 --- /dev/null +++ b/Sources/PackageModel/InstalledLibrariesSupport/LibraryMetadata.swift @@ -0,0 +1,54 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Basics +import Foundation + +public struct LibraryMetadata: Decodable { + public enum Identity: Equatable, Decodable { + case packageIdentity(scope: String, name: String) + case sourceControl(url: SourceControlURL) + } + + /// The package from which it was built (e.g., the URL https://github.com/apple/swift-syntax.git) + public let identities: [Identity] + /// The version that was built (e.g., 509.0.2) + public let version: String + /// The product name, if it differs from the module name (e.g., SwiftParser). + public let productName: String? + + let schemaVersion: Int +} + +extension LibraryMetadata.Identity { + public var identity: PackageIdentity { + switch self { + case .packageIdentity(let scope, let name): + return PackageIdentity.plain("\(scope)/\(name)") + case .sourceControl(let url): + return PackageIdentity(url: url) + } + } + + public var kind: PackageReference.Kind { + switch self { + case .packageIdentity: + return .registry(self.identity) + case .sourceControl(let url): + return .remoteSourceControl(.init(url.absoluteString)) + } + } + + public var ref: PackageReference { + return PackageReference(identity: self.identity, kind: self.kind) + } +} diff --git a/Sources/PackageModel/InstalledLibrariesSupport/provided-libraries.json b/Sources/PackageModel/InstalledLibrariesSupport/provided-libraries.json new file mode 100644 index 00000000000..02d798442b7 --- /dev/null +++ b/Sources/PackageModel/InstalledLibrariesSupport/provided-libraries.json @@ -0,0 +1,16 @@ +[ + { + "identities": + [ + { + "sourceControl": + { + "url": {"urlString": "https://github.com/apple/swift-testing.git"} + } + } + ], + "productName": "Testing", + "version": "0.4.0", + "schemaVersion": 1 + } +] diff --git a/Sources/PackageModel/Toolchain.swift b/Sources/PackageModel/Toolchain.swift index 63fe3a4efac..cd7db9c68ba 100644 --- a/Sources/PackageModel/Toolchain.swift +++ b/Sources/PackageModel/Toolchain.swift @@ -43,6 +43,9 @@ public protocol Toolchain { /// Configuration from the used toolchain. var installedSwiftPMConfiguration: InstalledSwiftPMConfiguration { get } + /// Metadata for libraries provided by the used toolchain. + var providedLibraries: [LibraryMetadata] { get } + /// The root path to the Swift SDK used by this toolchain. var sdkRootPath: AbsolutePath? { get } diff --git a/Sources/PackageModel/UserToolchain.swift b/Sources/PackageModel/UserToolchain.swift index fbb9013c3f4..58b24986c2f 100644 --- a/Sources/PackageModel/UserToolchain.swift +++ b/Sources/PackageModel/UserToolchain.swift @@ -88,6 +88,8 @@ public final class UserToolchain: Toolchain { public let installedSwiftPMConfiguration: InstalledSwiftPMConfiguration + public let providedLibraries: [LibraryMetadata] + /// 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 @@ -484,7 +486,8 @@ public final class UserToolchain: Toolchain { environment: EnvironmentVariables = .process(), searchStrategy: SearchStrategy = .default, customLibrariesLocation: ToolchainConfiguration.SwiftPMLibrariesLocation? = nil, - customInstalledSwiftPMConfiguration: InstalledSwiftPMConfiguration? = nil + customInstalledSwiftPMConfiguration: InstalledSwiftPMConfiguration? = nil, + customProvidedLibraries: [LibraryMetadata]? = nil ) throws { self.swiftSDK = swiftSDK self.environment = environment @@ -539,6 +542,33 @@ public final class UserToolchain: Toolchain { } } + if let customProvidedLibraries { + self.providedLibraries = customProvidedLibraries + } else { + // When building with CMake, we need to skip resource support. + #if SKIP_RESOURCE_SUPPORT + let path = self.swiftCompilerPath.parentDirectory.parentDirectory.appending(components: ["share", "pm", "provided-libraries.json"]) + #else + let path: AbsolutePath + if let developmentPath = Bundle.module.path(forResource: "provided-libraries", ofType: "json") { + // During development, we should be able to find the metadata file using `Bundle.module`. + path = try AbsolutePath(validating: developmentPath) + } else { + // When deployed, we can find the metadata file in the toolchain. + path = self.swiftCompilerPath.parentDirectory.parentDirectory.appending(components: ["share", "pm", "provided-libraries.json"]) + } + #endif + if localFileSystem.exists(path) { + self.providedLibraries = try JSONDecoder.makeWithDefaults().decode( + path: path, + fileSystem: localFileSystem, + as: [LibraryMetadata].self + ) + } else { + self.providedLibraries = [] + } + } + // Use the triple from Swift SDK or compute the host triple using swiftc. var triple = try swiftSDK.targetTriple ?? Triple.getHostTriple(usingSwiftCompiler: swiftCompilers.compile) diff --git a/Sources/SPMTestSupport/MockBuildTestHelper.swift b/Sources/SPMTestSupport/MockBuildTestHelper.swift index d18050e953d..2aeb77764ff 100644 --- a/Sources/SPMTestSupport/MockBuildTestHelper.swift +++ b/Sources/SPMTestSupport/MockBuildTestHelper.swift @@ -38,6 +38,7 @@ public struct MockToolchain: PackageModel.Toolchain { public let swiftPluginServerPath: AbsolutePath? = nil public let extraFlags = PackageModel.BuildFlags() public let installedSwiftPMConfiguration = InstalledSwiftPMConfiguration.default + public let providedLibraries = [LibraryMetadata]() public func getClangCompiler() throws -> AbsolutePath { "/fake/path/to/clang" diff --git a/Sources/SPMTestSupport/MockWorkspace.swift b/Sources/SPMTestSupport/MockWorkspace.swift index 29e98d45f86..6d21ea9f8d2 100644 --- a/Sources/SPMTestSupport/MockWorkspace.swift +++ b/Sources/SPMTestSupport/MockWorkspace.swift @@ -364,7 +364,13 @@ public final class MockWorkspace { observability.topScope.trap { let rootInput = PackageGraphRootInput(packages: try rootPaths(for: roots)) let ws = try self.getOrCreateWorkspace() - try ws.unedit(packageName: packageName, forceRemove: forceRemove, root: rootInput, observabilityScope: observability.topScope) + try ws.unedit( + packageName: packageName, + forceRemove: forceRemove, + root: rootInput, + availableLibraries: [], // assume no provided libraries for testing. + observabilityScope: observability.topScope + ) } result(observability.diagnostics) } @@ -461,6 +467,7 @@ public final class MockWorkspace { let graph = try workspace.loadPackageGraph( rootInput: rootInput, forceResolvedVersions: forceResolvedVersions, + availableLibraries: [], // assume no provided libraries for testing. expectedSigningEntities: expectedSigningEntities, observabilityScope: observability.topScope ) @@ -499,6 +506,7 @@ public final class MockWorkspace { try workspace.loadPackageGraph( rootInput: rootInput, forceResolvedVersions: forceResolvedVersions, + availableLibraries: [], // assume no provided libraries for testing. observabilityScope: observability.topScope ) } @@ -522,13 +530,18 @@ public final class MockWorkspace { ) let root = PackageGraphRoot(input: rootInput, manifests: rootManifests, observabilityScope: observability.topScope) - let dependencyManifests = try workspace.loadDependencyManifests(root: root, observabilityScope: observability.topScope) + let dependencyManifests = try workspace.loadDependencyManifests( + root: root, + availableLibraries: [], // assume no provided libraries for testing. + observabilityScope: observability.topScope + ) let result = try workspace.precomputeResolution( root: root, dependencyManifests: dependencyManifests, pinsStore: pinsStore, constraints: [], + availableLibraries: [], // assume no provided libraries for testing. observabilityScope: observability.topScope ) @@ -737,7 +750,11 @@ public final class MockWorkspace { ) let rootManifests = try temp_await { workspace.loadRootManifests(packages: rootInput.packages, observabilityScope: observability.topScope, completion: $0) } let graphRoot = PackageGraphRoot(input: rootInput, manifests: rootManifests, observabilityScope: observability.topScope) - let manifests = try workspace.loadDependencyManifests(root: graphRoot, observabilityScope: observability.topScope) + let manifests = try workspace.loadDependencyManifests( + root: graphRoot, + availableLibraries: [], // assume no provided libraries for testing. + observabilityScope: observability.topScope + ) result(manifests, observability.diagnostics) } diff --git a/Sources/SPMTestSupport/Resolver.swift b/Sources/SPMTestSupport/Resolver.swift new file mode 100644 index 00000000000..f86371ce196 --- /dev/null +++ b/Sources/SPMTestSupport/Resolver.swift @@ -0,0 +1,19 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import PackageGraph + +extension PubGrubDependencyResolver { + public func solve(constraints: [Constraint]) -> Result<[DependencyResolverBinding], Error> { + return solve(constraints: constraints, availableLibraries: [], preferPrebuiltLibraries: false) + } +} diff --git a/Sources/SPMTestSupport/misc.swift b/Sources/SPMTestSupport/misc.swift index 938343b7540..2c357dcc375 100644 --- a/Sources/SPMTestSupport/misc.swift +++ b/Sources/SPMTestSupport/misc.swift @@ -356,6 +356,7 @@ public func loadPackageGraph( shouldCreateMultipleTestProducts: shouldCreateMultipleTestProducts, createREPLProduct: createREPLProduct, customXCTestMinimumDeploymentTargets: customXCTestMinimumDeploymentTargets, + availableLibraries: [], fileSystem: fileSystem, observabilityScope: observabilityScope ) diff --git a/Sources/Workspace/ResolverPrecomputationProvider.swift b/Sources/Workspace/ResolverPrecomputationProvider.swift index c5eaca741e8..3d9803adea7 100644 --- a/Sources/Workspace/ResolverPrecomputationProvider.swift +++ b/Sources/Workspace/ResolverPrecomputationProvider.swift @@ -44,14 +44,19 @@ struct ResolverPrecomputationProvider: PackageContainerProvider { /// The tools version currently in use. let currentToolsVersion: ToolsVersion + /// The available libraries in the SDK. + let availableLibraries: [LibraryMetadata] + init( root: PackageGraphRoot, dependencyManifests: Workspace.DependencyManifests, - currentToolsVersion: ToolsVersion = ToolsVersion.current + currentToolsVersion: ToolsVersion = ToolsVersion.current, + availableLibraries: [LibraryMetadata] ) { self.root = root self.dependencyManifests = dependencyManifests self.currentToolsVersion = currentToolsVersion + self.availableLibraries = availableLibraries } func getContainer( @@ -84,6 +89,15 @@ struct ResolverPrecomputationProvider: PackageContainerProvider { return completion(.success(container)) } + // Match against available prebuilt libraries. + if let matchingPrebuiltLibrary = package.matchingPrebuiltLibrary(in: availableLibraries) { + do { + return completion(.success(try PrebuiltPackageContainer(metadata: matchingPrebuiltLibrary))) + } catch { + return completion(.failure(error)) + } + } + // As we don't have anything else locally, error out. completion(.failure(ResolverPrecomputationError.missingPackage(package: package))) } diff --git a/Sources/Workspace/Workspace+Dependencies.swift b/Sources/Workspace/Workspace+Dependencies.swift index 7c62fc49fcf..3157260e268 100644 --- a/Sources/Workspace/Workspace+Dependencies.swift +++ b/Sources/Workspace/Workspace+Dependencies.swift @@ -37,6 +37,7 @@ import class PackageGraph.PinsStore import struct PackageGraph.PubGrubDependencyResolver import struct PackageGraph.Term import class PackageLoading.ManifestLoader +import struct PackageModel.LibraryMetadata import enum PackageModel.PackageDependency import struct PackageModel.PackageIdentity import struct PackageModel.PackageReference @@ -56,6 +57,7 @@ extension Workspace { root: PackageGraphRootInput, packages: [String] = [], dryRun: Bool = false, + availableLibraries: [LibraryMetadata], observabilityScope: ObservabilityScope ) throws -> [(PackageReference, Workspace.PackageStateChange)]? { let start = DispatchTime.now() @@ -84,7 +86,11 @@ extension Workspace { dependencyMapper: self.dependencyMapper, observabilityScope: observabilityScope ) - let currentManifests = try self.loadDependencyManifests(root: graphRoot, observabilityScope: observabilityScope) + let currentManifests = try self.loadDependencyManifests( + root: graphRoot, + availableLibraries: availableLibraries, + observabilityScope: observabilityScope + ) // Abort if we're unable to load the pinsStore or have any diagnostics. guard let pinsStore = observabilityScope.trap({ try self.pinsStore.load() }) else { return nil } @@ -120,6 +126,7 @@ extension Workspace { let updateResults = self.resolveDependencies( resolver: resolver, + availableLibraries: availableLibraries, constraints: updateConstraints, observabilityScope: observabilityScope ) @@ -156,6 +163,7 @@ extension Workspace { // Load the updated manifests. let updatedDependencyManifests = try self.loadDependencyManifests( root: graphRoot, + availableLibraries: availableLibraries, observabilityScope: observabilityScope ) // If we have missing packages, something is fundamentally wrong with the resolution of the graph @@ -189,6 +197,7 @@ extension Workspace { func _resolve( root: PackageGraphRootInput, explicitProduct: String?, + availableLibraries: [LibraryMetadata], resolvedFileStrategy: ResolvedFileStrategy, observabilityScope: ObservabilityScope ) throws -> DependencyManifests { @@ -204,6 +213,7 @@ extension Workspace { return try self._resolveBasedOnResolvedVersionsFile( root: root, explicitProduct: explicitProduct, + availableLibraries: availableLibraries, observabilityScope: observabilityScope ) case .update(let forceResolution): @@ -240,6 +250,7 @@ extension Workspace { let (manifests, precomputationResult) = try self.tryResolveBasedOnResolvedVersionsFile( root: root, explicitProduct: explicitProduct, + availableLibraries: availableLibraries, observabilityScope: observabilityScope ) switch precomputationResult { @@ -263,6 +274,7 @@ extension Workspace { return try self.resolveAndUpdateResolvedFile( root: root, explicitProduct: explicitProduct, + availableLibraries: availableLibraries, forceResolution: forceResolution, constraints: [], observabilityScope: observabilityScope @@ -289,11 +301,13 @@ extension Workspace { func _resolveBasedOnResolvedVersionsFile( root: PackageGraphRootInput, explicitProduct: String?, + availableLibraries: [LibraryMetadata], observabilityScope: ObservabilityScope ) throws -> DependencyManifests { let (manifests, precomputationResult) = try self.tryResolveBasedOnResolvedVersionsFile( root: root, explicitProduct: explicitProduct, + availableLibraries: availableLibraries, observabilityScope: observabilityScope ) switch precomputationResult { @@ -326,6 +340,7 @@ extension Workspace { fileprivate func tryResolveBasedOnResolvedVersionsFile( root: PackageGraphRootInput, explicitProduct: String?, + availableLibraries: [LibraryMetadata], observabilityScope: ObservabilityScope ) throws -> (DependencyManifests, ResolutionPrecomputationResult) { // Ensure the cache path exists. @@ -350,7 +365,11 @@ extension Workspace { !observabilityScope.errorsReported else { return try ( - self.loadDependencyManifests(root: graphRoot, observabilityScope: observabilityScope), + self.loadDependencyManifests( + root: graphRoot, + availableLibraries: availableLibraries, + observabilityScope: observabilityScope + ), .notRequired ) } @@ -441,6 +460,7 @@ extension Workspace { let currentManifests = try self.loadDependencyManifests( root: graphRoot, automaticallyAddManagedDependencies: true, + availableLibraries: availableLibraries, observabilityScope: observabilityScope ) @@ -455,6 +475,7 @@ extension Workspace { dependencyManifests: currentManifests, pinsStore: pinsStore, constraints: [], + availableLibraries: availableLibraries, observabilityScope: observabilityScope ) @@ -471,6 +492,7 @@ extension Workspace { func resolveAndUpdateResolvedFile( root: PackageGraphRootInput, explicitProduct: String? = nil, + availableLibraries: [LibraryMetadata], forceResolution: Bool, constraints: [PackageContainerConstraint], observabilityScope: ObservabilityScope @@ -496,7 +518,11 @@ extension Workspace { dependencyMapper: self.dependencyMapper, observabilityScope: observabilityScope ) - let currentManifests = try self.loadDependencyManifests(root: graphRoot, observabilityScope: observabilityScope) + let currentManifests = try self.loadDependencyManifests( + root: graphRoot, + availableLibraries: availableLibraries, + observabilityScope: observabilityScope + ) guard !observabilityScope.errorsReported else { return currentManifests } @@ -531,6 +557,7 @@ extension Workspace { dependencyManifests: currentManifests, pinsStore: pinsStore, constraints: constraints, + availableLibraries: availableLibraries, observabilityScope: observabilityScope ) @@ -569,6 +596,7 @@ extension Workspace { let result = self.resolveDependencies( resolver: resolver, + availableLibraries: availableLibraries, constraints: computedConstraints, observabilityScope: observabilityScope ) @@ -593,6 +621,7 @@ extension Workspace { // Update the pinsStore. let updatedDependencyManifests = try self.loadDependencyManifests( root: graphRoot, + availableLibraries: availableLibraries, observabilityScope: observabilityScope ) // If we still have missing packages, something is fundamentally wrong with the resolution of the graph @@ -801,6 +830,7 @@ extension Workspace { dependencyManifests: DependencyManifests, pinsStore: PinsStore, constraints: [PackageContainerConstraint], + availableLibraries: [LibraryMetadata], observabilityScope: ObservabilityScope ) throws -> ResolutionPrecomputationResult { let computedConstraints = @@ -812,14 +842,19 @@ extension Workspace { let precomputationProvider = ResolverPrecomputationProvider( root: root, - dependencyManifests: dependencyManifests + dependencyManifests: dependencyManifests, + availableLibraries: availableLibraries ) let resolver = PubGrubDependencyResolver( provider: precomputationProvider, pins: pinsStore.pins, observabilityScope: observabilityScope ) - let result = resolver.solve(constraints: computedConstraints) + let result = resolver.solve( + constraints: computedConstraints, + availableLibraries: availableLibraries, + preferPrebuiltLibraries: true + ) guard !observabilityScope.errorsReported else { return .required(reason: .errorsPreviouslyReported) @@ -1113,11 +1148,24 @@ extension Workspace { /// Runs the dependency resolver based on constraints provided and returns the results. fileprivate func resolveDependencies( resolver: PubGrubDependencyResolver, + availableLibraries: [LibraryMetadata], constraints: [PackageContainerConstraint], observabilityScope: ObservabilityScope ) -> [DependencyResolverBinding] { os_signpost(.begin, name: SignpostName.pubgrub) - let result = resolver.solve(constraints: constraints) + var result = resolver.solve( + constraints: constraints, + availableLibraries: availableLibraries, + preferPrebuiltLibraries: true + ) + // If the initial resolution failed due to prebuilt libraries, we try to resolve again without prebuilt libraries. + if case let Result.failure(error as PubGrubDependencyResolver.PubgrubError) = result, case .potentiallyUnresovableDueToPrebuiltLibrary = error { + result = resolver.solve( + constraints: constraints, + availableLibraries: availableLibraries, + preferPrebuiltLibraries: false + ) + } os_signpost(.end, name: SignpostName.pubgrub) // Take an action based on the result. diff --git a/Sources/Workspace/Workspace+Editing.swift b/Sources/Workspace/Workspace+Editing.swift index 1b4d1442617..c27afe324a6 100644 --- a/Sources/Workspace/Workspace+Editing.swift +++ b/Sources/Workspace/Workspace+Editing.swift @@ -15,6 +15,7 @@ import class Basics.ObservabilityScope import struct Basics.RelativePath import func Basics.temp_await import struct PackageGraph.PackageGraphRootInput +import struct PackageModel.LibraryMetadata import struct SourceControl.Revision import class TSCBasic.InMemoryFileSystem @@ -169,6 +170,7 @@ extension Workspace { dependency: ManagedDependency, forceRemove: Bool, root: PackageGraphRootInput? = nil, + availableLibraries: [LibraryMetadata], observabilityScope: ObservabilityScope ) throws { // Compute if we need to force remove. @@ -233,6 +235,7 @@ extension Workspace { try self._resolve( root: root, explicitProduct: .none, + availableLibraries: availableLibraries, resolvedFileStrategy: .update(forceResolution: false), observabilityScope: observabilityScope ) diff --git a/Sources/Workspace/Workspace+Manifests.swift b/Sources/Workspace/Workspace+Manifests.swift index 35eb95c863f..64646669616 100644 --- a/Sources/Workspace/Workspace+Manifests.swift +++ b/Sources/Workspace/Workspace+Manifests.swift @@ -28,6 +28,7 @@ import struct PackageGraph.PackageGraphRoot import class PackageLoading.ManifestLoader import struct PackageLoading.ManifestValidator import struct PackageLoading.ToolsVersionParser +import struct PackageModel.LibraryMetadata import class PackageModel.Manifest import struct PackageModel.PackageIdentity import struct PackageModel.PackageReference @@ -60,6 +61,8 @@ extension Workspace { private let workspace: Workspace + private let availableLibraries: [LibraryMetadata] + private let observabilityScope: ObservabilityScope private let _dependencies: LoadableResult<( @@ -78,17 +81,20 @@ extension Workspace { fileSystem: FileSystem )], workspace: Workspace, + availableLibraries: [LibraryMetadata], observabilityScope: ObservabilityScope ) { self.root = root self.dependencies = dependencies self.workspace = workspace + self.availableLibraries = availableLibraries self.observabilityScope = observabilityScope self._dependencies = LoadableResult { try Self.computeDependencies( root: root, dependencies: dependencies, workspace: workspace, + availableLibraries: availableLibraries, observabilityScope: observabilityScope ) } @@ -160,6 +166,7 @@ extension Workspace { fileSystem: FileSystem )], workspace: Workspace, + availableLibraries: [LibraryMetadata], observabilityScope: ObservabilityScope ) throws -> ( @@ -175,6 +182,32 @@ extension Workspace { } ) + let availableIdentities: Set = try Set(manifestsMap.map { + // FIXME: adding this guard to ensure refactoring is correct 9/21 + // we only care about remoteSourceControl for this validation. it would otherwise trigger for + // a dependency is put into edit mode, which we want to deprecate anyways + if case .remoteSourceControl = $0.1.packageKind { + let effectiveURL = workspace.mirrors.effective(for: $0.1.packageLocation) + guard effectiveURL == $0.1.packageKind.locationString else { + throw InternalError( + "effective url for \($0.1.packageLocation) is \(effectiveURL), different from expected \($0.1.packageKind.locationString)" + ) + } + } + return PackageReference(identity: $0.key, kind: $0.1.packageKind) + }) + + let identitiesAvailableInSDK = availableLibraries.flatMap { + $0.identities.map { + $0.ref + }.filter { + // We "trust the process" here, if an identity from the SDK is available, filter it. + !availableIdentities.contains($0) + }.map { + $0.identity + } + } + var inputIdentities: OrderedCollections.OrderedSet = [] let inputNodes: [GraphLoadingNode] = root.packages.map { identity, package in inputIdentities.append(package.reference) @@ -252,20 +285,11 @@ extension Workspace { } requiredIdentities = inputIdentities.union(requiredIdentities) - let availableIdentities: Set = try Set(manifestsMap.map { - // FIXME: adding this guard to ensure refactoring is correct 9/21 - // we only care about remoteSourceControl for this validation. it would otherwise trigger for - // a dependency is put into edit mode, which we want to deprecate anyways - if case .remoteSourceControl = $0.1.packageKind { - let effectiveURL = workspace.mirrors.effective(for: $0.1.packageLocation) - guard effectiveURL == $0.1.packageKind.locationString else { - throw InternalError( - "effective url for \($0.1.packageLocation) is \(effectiveURL), different from expected \($0.1.packageKind.locationString)" - ) - } - } - return PackageReference(identity: $0.key, kind: $0.1.packageKind) - }) + let identitiesToFilter = requiredIdentities.filter { + return identitiesAvailableInSDK.contains($0.identity) + } + requiredIdentities = requiredIdentities.subtracting(identitiesToFilter) + // We should never have loaded a manifest we don't need. assert( availableIdentities.isSubset(of: requiredIdentities), @@ -405,6 +429,7 @@ extension Workspace { public func loadDependencyManifests( root: PackageGraphRoot, automaticallyAddManagedDependencies: Bool = false, + availableLibraries: [LibraryMetadata], observabilityScope: ObservabilityScope ) throws -> DependencyManifests { let prepopulateManagedDependencies: ([PackageReference]) throws -> Void = { refs in @@ -446,13 +471,17 @@ extension Workspace { } // Validates that all the managed dependencies are still present in the file system. - self.fixManagedDependencies(observabilityScope: observabilityScope) + self.fixManagedDependencies( + availableLibraries: availableLibraries, + observabilityScope: observabilityScope + ) guard !observabilityScope.errorsReported else { // return partial results return DependencyManifests( root: root, dependencies: [], workspace: self, + availableLibraries: availableLibraries, observabilityScope: observabilityScope ) } @@ -526,6 +555,7 @@ extension Workspace { root: root, dependencies: [], workspace: self, + availableLibraries: availableLibraries, observabilityScope: observabilityScope ) } @@ -586,6 +616,7 @@ extension Workspace { root: root, dependencies: dependencies, workspace: self, + availableLibraries: availableLibraries, observabilityScope: observabilityScope ) } @@ -777,7 +808,10 @@ extension Workspace { /// If some checkout dependency is removed form the file system, clone it again. /// If some edited dependency is removed from the file system, mark it as unedited and /// fallback on the original checkout. - private func fixManagedDependencies(observabilityScope: ObservabilityScope) { + private func fixManagedDependencies( + availableLibraries: [LibraryMetadata], + observabilityScope: ObservabilityScope + ) { // Reset managed dependencies if the state file was removed during the lifetime of the Workspace object. if !self.state.dependencies.isEmpty && !self.state.stateFileExists() { try? self.state.reset() @@ -846,7 +880,12 @@ extension Workspace { // Note: We don't resolve the dependencies when unediting // here because we expect this method to be called as part // of some other resolve operation (i.e. resolve, update, etc). - try self.unedit(dependency: dependency, forceRemove: true, observabilityScope: observabilityScope) + try self.unedit( + dependency: dependency, + forceRemove: true, + availableLibraries: availableLibraries, + observabilityScope: observabilityScope + ) observabilityScope .emit(.editedDependencyMissing(packageName: dependency.packageRef.identity.description)) diff --git a/Sources/Workspace/Workspace.swift b/Sources/Workspace/Workspace.swift index 92250c85d15..ea93cf4ece4 100644 --- a/Sources/Workspace/Workspace.swift +++ b/Sources/Workspace/Workspace.swift @@ -571,6 +571,11 @@ public class Workspace { initializationWarningHandler: initializationWarningHandler ) } + + fileprivate var providedLibraries: [LibraryMetadata] { + // Note: Eventually, we should get these from the individual SDKs, but the first step is providing the metadata centrally in the toolchain. + return self.hostToolchain.providedLibraries + } } // MARK: - Public API @@ -621,6 +626,7 @@ extension Workspace { packageName: String, forceRemove: Bool, root: PackageGraphRootInput, + availableLibraries: [LibraryMetadata], observabilityScope: ObservabilityScope ) throws { guard let dependency = self.state.dependencies[.plain(packageName)] else { @@ -637,6 +643,7 @@ extension Workspace { dependency: dependency, forceRemove: forceRemove, root: root, + availableLibraries: availableLibraries, observabilityScope: observabilityScope ) } @@ -657,6 +664,7 @@ extension Workspace { try self._resolve( root: root, explicitProduct: explicitProduct, + availableLibraries: self.providedLibraries, resolvedFileStrategy: forceResolvedVersions ? .lockFile : forceResolution ? .update(forceResolution: true) : .bestEffort, observabilityScope: observabilityScope @@ -728,6 +736,7 @@ extension Workspace { // Run the resolution. try self.resolveAndUpdateResolvedFile( root: root, + availableLibraries: self.providedLibraries, forceResolution: false, constraints: [constraint], observabilityScope: observabilityScope @@ -745,6 +754,7 @@ extension Workspace { try self._resolveBasedOnResolvedVersionsFile( root: root, explicitProduct: .none, + availableLibraries: self.providedLibraries, observabilityScope: observabilityScope ) } @@ -855,6 +865,7 @@ extension Workspace { root: root, packages: packages, dryRun: dryRun, + availableLibraries: self.providedLibraries, observabilityScope: observabilityScope ) } @@ -866,6 +877,7 @@ extension Workspace { forceResolvedVersions: Bool = false, customXCTestMinimumDeploymentTargets: [PackageModel.Platform: PlatformVersion]? = .none, testEntryPointPath: AbsolutePath? = nil, + availableLibraries: [LibraryMetadata], expectedSigningEntities: [PackageIdentity: RegistryReleaseMetadata.SigningEntity] = [:], observabilityScope: ObservabilityScope ) throws -> PackageGraph { @@ -885,6 +897,7 @@ extension Workspace { let manifests = try self._resolve( root: root, explicitProduct: explicitProduct, + availableLibraries: availableLibraries, resolvedFileStrategy: forceResolvedVersions ? .lockFile : .bestEffort, observabilityScope: observabilityScope ) @@ -911,6 +924,7 @@ extension Workspace { createREPLProduct: self.configuration.createREPLProduct, customXCTestMinimumDeploymentTargets: customXCTestMinimumDeploymentTargets, testEntryPointPath: testEntryPointPath, + availableLibraries: self.providedLibraries, fileSystem: self.fileSystem, observabilityScope: observabilityScope ) @@ -932,6 +946,7 @@ extension Workspace { try self.loadPackageGraph( rootInput: PackageGraphRootInput(packages: [rootPath]), explicitProduct: explicitProduct, + availableLibraries: self.providedLibraries, observabilityScope: observabilityScope ) } diff --git a/Sources/swift-bootstrap/main.swift b/Sources/swift-bootstrap/main.swift index 7891cd06ade..b879f9a79bb 100644 --- a/Sources/swift-bootstrap/main.swift +++ b/Sources/swift-bootstrap/main.swift @@ -314,6 +314,8 @@ struct SwiftBootstrapBuildTool: ParsableCommand { packageGraphLoader: packageGraphLoader, additionalFileRules: [], pkgConfigDirectories: [], + dependenciesByRootPackageIdentity: [:], + targetsByRootPackageIdentity: [:], outputStream: TSCBasic.stdoutStream, logLevel: logLevel, fileSystem: self.fileSystem, @@ -378,6 +380,7 @@ struct SwiftBootstrapBuildTool: ParsableCommand { partial[item.key] = (manifest: item.value, fs: self.fileSystem) }, binaryArtifacts: [:], + availableLibraries: [], // assume no provided libraries during bootstrap fileSystem: fileSystem, observabilityScope: observabilityScope ) diff --git a/Tests/BuildTests/BuildOperationTests.swift b/Tests/BuildTests/BuildOperationTests.swift new file mode 100644 index 00000000000..3f196dd41fa --- /dev/null +++ b/Tests/BuildTests/BuildOperationTests.swift @@ -0,0 +1,63 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +@testable import Build +@testable import PackageModel + +import Basics +import SPMTestSupport +import XCTest + +import class TSCBasic.BufferedOutputByteStream +import class TSCBasic.InMemoryFileSystem + +final class BuildOperationTests: XCTestCase { + func testDetectUnexpressedDependencies() throws { + let fs = InMemoryFileSystem(files: [ + "/path/to/build/debug/Lunch.build/Lunch.d" : "/Best.framework" + ]) + + let observability = ObservabilitySystem.makeForTesting() + let buildOp = BuildOperation( + productsBuildParameters: mockBuildParameters(shouldDisableLocalRpath: false), + toolsBuildParameters: mockBuildParameters(shouldDisableLocalRpath: false), + cacheBuildManifest: false, + packageGraphLoader: { fatalError() }, + additionalFileRules: [], + pkgConfigDirectories: [], + dependenciesByRootPackageIdentity: [:], + targetsByRootPackageIdentity: [:], + outputStream: BufferedOutputByteStream(), + logLevel: .info, + fileSystem: fs, + observabilityScope: observability.topScope + ) + buildOp.detectUnexpressedDependencies( + availableLibraries: [ + .init( + identities: [ + .sourceControl(url: .init("https://example.com/org/foo")) + ], + version: "1.0.0", + productName: "Best", + schemaVersion: 1 + ) + ], + targetDependencyMap: ["Lunch": []] + ) + + XCTAssertEqual( + observability.diagnostics.map { $0.message }, + ["target 'Lunch' has an unexpressed depedency on 'foo'"] + ) + } +} diff --git a/Tests/CommandsTests/PackageToolTests.swift b/Tests/CommandsTests/PackageToolTests.swift index 1fc2d01d9e7..4d91794c8bd 100644 --- a/Tests/CommandsTests/PackageToolTests.swift +++ b/Tests/CommandsTests/PackageToolTests.swift @@ -3205,7 +3205,11 @@ final class PackageToolTests: CommandsTestCase { XCTAssert(rootManifests.count == 1, "\(rootManifests)") // Load the package graph. - let _ = try workspace.loadPackageGraph(rootInput: rootInput, observabilityScope: observability.topScope) + let _ = try workspace.loadPackageGraph( + rootInput: rootInput, + availableLibraries: [], // assume no provided libraries for testing. + observabilityScope: observability.topScope + ) XCTAssertNoDiagnostics(observability.diagnostics) } } diff --git a/Tests/FunctionalTests/PluginTests.swift b/Tests/FunctionalTests/PluginTests.swift index e2d5e03e827..5636c6dd41b 100644 --- a/Tests/FunctionalTests/PluginTests.swift +++ b/Tests/FunctionalTests/PluginTests.swift @@ -442,7 +442,11 @@ class PluginTests: XCTestCase { XCTAssert(rootManifests.count == 1, "\(rootManifests)") // Load the package graph. - let packageGraph = try workspace.loadPackageGraph(rootInput: rootInput, observabilityScope: observability.topScope) + let packageGraph = try workspace.loadPackageGraph( + rootInput: rootInput, + availableLibraries: [], // assume no provided libraries for testing. + observabilityScope: observability.topScope + ) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssert(packageGraph.packages.count == 2, "\(packageGraph.packages)") XCTAssert(packageGraph.rootPackages.count == 1, "\(packageGraph.rootPackages)") @@ -625,7 +629,11 @@ class PluginTests: XCTestCase { XCTAssert(rootManifests.count == 1, "\(rootManifests)") // Load the package graph. - let packageGraph = try workspace.loadPackageGraph(rootInput: rootInput, observabilityScope: observability.topScope) + let packageGraph = try workspace.loadPackageGraph( + rootInput: rootInput, + availableLibraries: [], // assume no provided libraries for testing. + observabilityScope: observability.topScope + ) XCTAssertNoDiagnostics(observability.diagnostics) // Make sure that the use of plugins doesn't bleed into the use of plugins by tools. @@ -719,7 +727,11 @@ class PluginTests: XCTestCase { XCTAssert(rootManifests.count == 1, "\(rootManifests)") // Load the package graph. - let packageGraph = try workspace.loadPackageGraph(rootInput: rootInput, observabilityScope: observability.topScope) + let packageGraph = try workspace.loadPackageGraph( + rootInput: rootInput, + availableLibraries: [], // assume no provided libraries for testing. + observabilityScope: observability.topScope + ) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssert(packageGraph.packages.count == 1, "\(packageGraph.packages)") XCTAssert(packageGraph.rootPackages.count == 1, "\(packageGraph.rootPackages)") @@ -1031,7 +1043,11 @@ class PluginTests: XCTestCase { XCTAssert(rootManifests.count == 1, "\(rootManifests)") // Load the package graph. - let packageGraph = try workspace.loadPackageGraph(rootInput: rootInput, observabilityScope: observability.topScope) + let packageGraph = try workspace.loadPackageGraph( + rootInput: rootInput, + availableLibraries: [], // assume no provided libraries for testing. + observabilityScope: observability.topScope + ) XCTAssert(packageGraph.packages.count == 4, "\(packageGraph.packages)") XCTAssert(packageGraph.rootPackages.count == 1, "\(packageGraph.rootPackages)") diff --git a/Tests/PackageGraphPerformanceTests/PackageGraphPerfTests.swift b/Tests/PackageGraphPerformanceTests/PackageGraphPerfTests.swift index 6cd5f755c80..facd1f603d2 100644 --- a/Tests/PackageGraphPerformanceTests/PackageGraphPerfTests.swift +++ b/Tests/PackageGraphPerformanceTests/PackageGraphPerfTests.swift @@ -94,6 +94,7 @@ final class PackageGraphPerfTests: XCTestCasePerf { identityResolver: identityResolver, externalManifests: externalManifests, binaryArtifacts: [:], + availableLibraries: [], // assume no provided libraries for testing. fileSystem: fs, observabilityScope: observability.topScope ) diff --git a/Tests/PackageGraphTests/PubgrubTests.swift b/Tests/PackageGraphTests/PubgrubTests.swift index 79285eb5f91..1cc30ae0cd6 100644 --- a/Tests/PackageGraphTests/PubgrubTests.swift +++ b/Tests/PackageGraphTests/PubgrubTests.swift @@ -1853,6 +1853,47 @@ final class PubGrubTestsBasicGraphs: XCTestCase { ("bar", .version(v1)), ]) } + + func testAvailableLibraries() throws { + let ref: PackageReference = .remoteSourceControl( + identity: .plain("foo"), + url: .init("https://example.com/org/foo") + ) + try builder.serve(ref, at: .version(.init(stringLiteral: "1.0.0"))) + try builder.serve(ref, at: .version(.init(stringLiteral: "1.2.0"))) + try builder.serve(ref, at: .version(.init(stringLiteral: "2.0.0"))) + + let resolver = builder.create() + let dependencies = try builder.create(dependencies: [ + "foo": (.versionSet(.range("1.0.0"..<"2.0.0")), .specific(["foo"])), + ]) + + let availableLibraries: [LibraryMetadata] = [ + .init( + identities: [.sourceControl(url: "https://example.com/org/foo")], + version: "1.0.0", + productName: nil, + schemaVersion: 1 + ) + ] + + let result = resolver.solve( + constraints: dependencies, + availableLibraries: availableLibraries, + preferPrebuiltLibraries: true + ) + // Available libraries are filtered from the resolver results, so this is expected to be empty. + AssertResult(result, []) + + let result2 = resolver.solve( + constraints: dependencies, + availableLibraries: availableLibraries, + preferPrebuiltLibraries: false + ) + AssertResult(result2, [ + ("foo", .version(.init(stringLiteral: "1.2.0"))), + ]) + } } final class PubGrubDiagnosticsTests: XCTestCase { diff --git a/Tests/SPMBuildCoreTests/PluginInvocationTests.swift b/Tests/SPMBuildCoreTests/PluginInvocationTests.swift index 490c6e421fe..d06d5bb133d 100644 --- a/Tests/SPMBuildCoreTests/PluginInvocationTests.swift +++ b/Tests/SPMBuildCoreTests/PluginInvocationTests.swift @@ -294,7 +294,11 @@ class PluginInvocationTests: XCTestCase { XCTAssert(rootManifests.count == 1, "\(rootManifests)") // Load the package graph. - let packageGraph = try workspace.loadPackageGraph(rootInput: rootInput, observabilityScope: observability.topScope) + let packageGraph = try workspace.loadPackageGraph( + rootInput: rootInput, + availableLibraries: [], // assume no provided libraries for testing. + observabilityScope: observability.topScope + ) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssert(packageGraph.packages.count == 1, "\(packageGraph.packages)") @@ -671,7 +675,11 @@ class PluginInvocationTests: XCTestCase { XCTAssert(rootManifests.count == 1, "\(rootManifests)") // Load the package graph. - XCTAssertThrowsError(try workspace.loadPackageGraph(rootInput: rootInput, observabilityScope: observability.topScope)) { error in + XCTAssertThrowsError(try workspace.loadPackageGraph( + rootInput: rootInput, + availableLibraries: [], // assume no provided libraries for testing. + observabilityScope: observability.topScope + )) { error in var diagnosed = false if let realError = error as? PackageGraphError, realError.description == "plugin 'MyPlugin' cannot depend on 'FooLib' of type 'library' from package 'foopackage'; this dependency is unsupported" { @@ -747,7 +755,10 @@ class PluginInvocationTests: XCTestCase { XCTAssert(rootManifests.count == 1, "\(rootManifests)") // Load the package graph. - XCTAssertThrowsError(try workspace.loadPackageGraph(rootInput: rootInput, observabilityScope: observability.topScope)) { error in + XCTAssertThrowsError(try workspace.loadPackageGraph( + rootInput: rootInput, + availableLibraries: [], // assume no provided libraries for testing. + observabilityScope: observability.topScope)) { error in var diagnosed = false if let realError = error as? PackageGraphError, realError.description == "plugin 'MyPlugin' cannot depend on 'MyLibrary' of type 'library'; this dependency is unsupported" { @@ -854,7 +865,11 @@ class PluginInvocationTests: XCTestCase { XCTAssert(rootManifests.count == 1, "\(rootManifests)") // Load the package graph. - let packageGraph = try workspace.loadPackageGraph(rootInput: rootInput, observabilityScope: observability.topScope) + let packageGraph = try workspace.loadPackageGraph( + rootInput: rootInput, + availableLibraries: [], // assume no provided libraries for testing. + observabilityScope: observability.topScope + ) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssert(packageGraph.packages.count == 1, "\(packageGraph.packages)") @@ -1034,7 +1049,11 @@ class PluginInvocationTests: XCTestCase { ) XCTAssert(rootManifests.count == 1, "\(rootManifests)") - let graph = try workspace.loadPackageGraph(rootInput: rootInput, observabilityScope: observability.topScope) + let graph = try workspace.loadPackageGraph( + rootInput: rootInput, + availableLibraries: [], // assume no provided libraries for testing. + observabilityScope: observability.topScope + ) let dict = try await workspace.loadPluginImports(packageGraph: graph) var count = 0 @@ -1179,7 +1198,11 @@ class PluginInvocationTests: XCTestCase { XCTAssert(rootManifests.count == 1, "\(rootManifests)") // Load the package graph. - let packageGraph = try workspace.loadPackageGraph(rootInput: rootInput, observabilityScope: observability.topScope) + let packageGraph = try workspace.loadPackageGraph( + rootInput: rootInput, + availableLibraries: [], // assume no provided libraries for testing. + observabilityScope: observability.topScope + ) XCTAssertNoDiagnostics(observability.diagnostics) // Find the build tool plugin. From 2fae93c611fcb502ada321e15c85d80274fc9072 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Sun, 18 Feb 2024 08:10:05 -0500 Subject: [PATCH 009/159] Set correct minimum OS targets for new packages using swift-testing (#7333) --- Sources/Workspace/InitPackage.swift | 4 ++-- Tests/WorkspaceTests/InitTests.swift | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Sources/Workspace/InitPackage.swift b/Sources/Workspace/InitPackage.swift index a03355ba247..77c1dadb46a 100644 --- a/Sources/Workspace/InitPackage.swift +++ b/Sources/Workspace/InitPackage.swift @@ -186,8 +186,8 @@ public final class InitPackage { var platforms = options.platforms - // Macros require macOS 10.15, iOS 13, etc. - if packageType == .macro { + // Macros and swift-testing require macOS 10.15, iOS 13, etc. + if packageType == .macro || options.supportedTestingLibraries.contains(.swiftTesting) { func addIfMissing(_ newPlatform: SupportedPlatform) { if platforms.contains(where: { platform in platform.platform == newPlatform.platform diff --git a/Tests/WorkspaceTests/InitTests.swift b/Tests/WorkspaceTests/InitTests.swift index 687f7f1ed01..473254d0232 100644 --- a/Tests/WorkspaceTests/InitTests.swift +++ b/Tests/WorkspaceTests/InitTests.swift @@ -170,6 +170,11 @@ class InitTests: XCTestCase { let manifest = path.appending("Package.swift") XCTAssertFileExists(manifest) let manifestContents: String = try localFileSystem.readFileContents(manifest) + XCTAssertMatch(manifestContents, .contains(#".macOS(.v10_15)"#)) + XCTAssertMatch(manifestContents, .contains(#".iOS(.v13)"#)) + XCTAssertMatch(manifestContents, .contains(#".tvOS(.v13)"#)) + XCTAssertMatch(manifestContents, .contains(#".watchOS(.v6)"#)) + XCTAssertMatch(manifestContents, .contains(#".macCatalyst(.v13)"#)) XCTAssertMatch(manifestContents, .contains(#"swift-testing.git", from: "0.2.0""#)) XCTAssertMatch(manifestContents, .contains(#".product(name: "Testing", package: "swift-testing")"#)) @@ -208,6 +213,11 @@ class InitTests: XCTestCase { let manifest = path.appending("Package.swift") XCTAssertFileExists(manifest) let manifestContents: String = try localFileSystem.readFileContents(manifest) + XCTAssertMatch(manifestContents, .contains(#".macOS(.v10_15)"#)) + XCTAssertMatch(manifestContents, .contains(#".iOS(.v13)"#)) + XCTAssertMatch(manifestContents, .contains(#".tvOS(.v13)"#)) + XCTAssertMatch(manifestContents, .contains(#".watchOS(.v6)"#)) + XCTAssertMatch(manifestContents, .contains(#".macCatalyst(.v13)"#)) XCTAssertMatch(manifestContents, .contains(#"swift-testing.git", from: "0.2.0""#)) XCTAssertMatch(manifestContents, .contains(#".product(name: "Testing", package: "swift-testing")"#)) From a0b25d33086fd1df849e702fcc6f0f9f1cd9e671 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Sun, 18 Feb 2024 19:29:06 +0000 Subject: [PATCH 010/159] Support macros when cross-compiling (#7118) ### Motivation: Not supporting macros in cross-compilation is a major limitation, especially for libraries like https://github.com/apple/swift-mmio. ### Modifications: Added `enum BuildTriple { case tools, destination }` and `var buildTriple: BuildTriple` on `ResolvedTarget` and `ResolvedProduct`. We're not using "host" and "target" triple terminology in this enum, as that clashes with build system "targets" and can lead to confusion in this context. Corresponding value is assigned to this property depending on target and product type: `tools` for macros, plugins, and their dependencies, `destination` for everything else (the default). Based on this property we can choose between `buildParameters.hostTriple` and `buildParameters.targetTriple` during build plan construction. Additionally, the resolved products and resolved targets graph is now constructed in a way that allows certain targets and products to be built twice: once for host triple, once for target triple if needed. This required modifying build description and build manifest generation to distinguish these products and targets from each other that are built twice. Artifacts built for the host now have `-tools` suffix appended to their names. This cascaded into making changes throughout the code base for build tool plugins and package plugins handling, which constructed their paths in an ad-hoc manner without accounting for possible changes to these names. Also added `CrossCompilationPackageGraphTests` and `CrossCompilationBuildPlanTests` to verify the changes made are applied correctly. ### Result: Resolves https://github.com/apple/swift-package-manager/issues/6950 Resolves rdar://105991372 --- CHANGELOG.md | 19 ++- .../Basics/Collections/IdentifiableSet.swift | 11 +- .../ClangTargetBuildDescription.swift | 9 +- .../ProductBuildDescription.swift | 4 +- .../ResolvedTarget+BuildDescription.swift | 23 +++ .../SwiftTargetBuildDescription.swift | 145 +++++++++++------- .../TargetBuildDescription.swift | 2 +- .../LLBuildManifestBuilder+Swift.swift | 36 +++-- .../LLBuildManifestBuilder.swift | 16 +- Sources/Build/BuildOperation.swift | 21 ++- .../Build/BuildPlan/BuildPlan+Product.swift | 8 +- Sources/Build/BuildPlan/BuildPlan+Swift.swift | 4 +- Sources/Build/BuildPlan/BuildPlan+Test.swift | 39 +++-- Sources/Build/BuildPlan/BuildPlan.swift | 85 +++++----- Sources/Build/CMakeLists.txt | 1 + .../Commands/PackageTools/PluginCommand.swift | 12 +- Sources/Commands/SwiftTestTool.swift | 12 +- .../Commands/Utilities/PluginDelegate.swift | 2 +- .../Utilities/SymbolGraphExtract.swift | 2 +- .../Commands/Utilities/TestingSupport.swift | 20 ++- Sources/Commands/Utilities/XCTEvents.swift | 18 +-- Sources/PackageGraph/BuildTriple.swift | 23 +++ .../PackageGraph/PackageGraph+Loading.swift | 23 +-- Sources/PackageGraph/PackageGraph.swift | 109 ++++++++++--- .../Resolution/ResolvedProduct.swift | 33 +++- .../Resolution/ResolvedTarget.swift | 33 +++- Sources/PackageModel/Target/Target.swift | 2 +- .../BuildParameters/BuildParameters.swift | 11 +- .../BuildSystem/BuildSystem.swift | 2 +- .../Plugins/PluginInvocation.swift | 4 +- .../SPMTestSupport/MockBuildTestHelper.swift | 42 +++-- .../SPMTestSupport/MockPackageGraphs.swift | 122 +++++++++++++++ .../SPMTestSupport/PackageGraphTester.swift | 71 ++++++++- Tests/BuildTests/BuildOperationTests.swift | 8 +- Tests/BuildTests/BuildPlanTests.swift | 113 ++++++++------ .../CrossCompilationBuildTests.swift | 81 ++++++++++ .../LLBuildManifestBuilderTests.swift | 58 +++++-- .../BuildTests/ModuleAliasingBuildTests.swift | 26 ++-- Tests/BuildTests/PluginsBuildPlanTests.swift | 26 +++- Tests/CommandsTests/PackageToolTests.swift | 47 ++++-- Tests/CommandsTests/SwiftToolTests.swift | 8 +- .../CrossCompilationPackageGraphTests.swift | 61 ++++++++ .../PackageGraphTests/PackageGraphTests.swift | 3 +- .../PluginInvocationTests.swift | 12 +- .../SourceKitLSPAPITests.swift | 29 +++- 45 files changed, 1080 insertions(+), 356 deletions(-) create mode 100644 Sources/Build/BuildDescription/ResolvedTarget+BuildDescription.swift create mode 100644 Sources/SPMTestSupport/MockPackageGraphs.swift create mode 100644 Tests/BuildTests/CrossCompilationBuildTests.swift create mode 100644 Tests/PackageGraphTests/CrossCompilationPackageGraphTests.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index fe5ed046fe5..61f95911802 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ Note: This is in reverse chronological order, so newer entries are added to the top. -Swift Next +Swift 6.0 ----------- * [#7202] @@ -8,11 +8,22 @@ Swift Next Package manifests can now access information about the Git repository the given package is in via the context object's `gitInformation` property. This allows to determine the current tag (if any), the current commit and whether or not there are uncommited changes. +* [#7201] + + `// swift-tools-version:` can now be specified on subsequent lines of `Package.swift`, for example when first few lines are required to contain a licensing comment header. + +* [#7118] + + Macros cross-compiled by SwiftPM with Swift SDKs are now correctly built, loaded, and evaluated for the host triple. + +Swift 5.10 +----------- + * [#7010] On macOS, `swift build` and `swift run` now produce binaries that allow backtraces in debug builds. Pass `SWIFT_BACKTRACE=enable=yes` environment variable to enable backtraces on such binaries when running them. -* [7101] +* [#7101] Binary artifacts are now cached along side repository checkouts so they do not need to be re-downloaded across projects. @@ -387,4 +398,8 @@ Swift 3.0 [#6276]: https://github.com/apple/swift-package-manager/pull/6276 [#6540]: https://github.com/apple/swift-package-manager/pull/6540 [#6663]: https://github.com/apple/swift-package-manager/pull/6663 +[#7010]: https://github.com/apple/swift-package-manager/pull/7010 [#7101]: https://github.com/apple/swift-package-manager/pull/7101 +[#7118]: https://github.com/apple/swift-package-manager/pull/7118 +[#7201]: https://github.com/apple/swift-package-manager/pull/7201 +[#7202]: https://github.com/apple/swift-package-manager/pull/7202 diff --git a/Sources/Basics/Collections/IdentifiableSet.swift b/Sources/Basics/Collections/IdentifiableSet.swift index b3bfec3071f..59bda6bdd85 100644 --- a/Sources/Basics/Collections/IdentifiableSet.swift +++ b/Sources/Basics/Collections/IdentifiableSet.swift @@ -45,13 +45,22 @@ public struct IdentifiableSet: Collection { } public subscript(id: Element.ID) -> Element? { - self.storage[id] + get { + self.storage[id] + } + set { + self.storage[id] = newValue + } } public func index(after i: Index) -> Index { Index(storageIndex: self.storage.index(after: i.storageIndex)) } + public mutating func insert(_ element: Element) { + self.storage[element.id] = element + } + public func union(_ otherSequence: some Sequence) -> Self { var result = self for element in otherSequence { diff --git a/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift b/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift index 69412b64fb3..cd37cd23cd1 100644 --- a/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift @@ -19,6 +19,9 @@ import struct SPMBuildCore.BuildParameters import struct SPMBuildCore.BuildToolPluginInvocationResult import struct SPMBuildCore.PrebuildCommandResult +@_spi(SwiftPMInternal) +import SPMBuildCore + import enum TSCBasic.ProcessEnv /// Target description for a Clang target i.e. C language family target. @@ -49,7 +52,7 @@ public final class ClangTargetBuildDescription { /// Path to the bundle generated for this module (if any). var bundlePath: AbsolutePath? { - guard !resources.isEmpty else { + guard !self.resources.isEmpty else { return .none } @@ -127,7 +130,7 @@ public final class ClangTargetBuildDescription { self.target = target self.toolsVersion = toolsVersion self.buildParameters = buildParameters - self.tempsPath = buildParameters.buildPath.appending(component: target.c99name + ".build") + self.tempsPath = target.tempsPath(buildParameters) self.derivedSources = Sources(paths: [], root: tempsPath.appending("DerivedSources")) // We did not use to apply package plugins to C-family targets in prior tools-versions, this preserves the behavior. @@ -219,7 +222,7 @@ public final class ClangTargetBuildDescription { if self.buildParameters.triple.isDarwin() { args += ["-fobjc-arc"] } - args += try buildParameters.targetTripleArgs(for: target) + args += try self.buildParameters.tripleArgs(for: target) args += optimizationArguments args += activeCompilationConditions diff --git a/Sources/Build/BuildDescription/ProductBuildDescription.swift b/Sources/Build/BuildDescription/ProductBuildDescription.swift index bb96cd383a2..d5e0e9ba8f9 100644 --- a/Sources/Build/BuildDescription/ProductBuildDescription.swift +++ b/Sources/Build/BuildDescription/ProductBuildDescription.swift @@ -311,7 +311,7 @@ public final class ProductBuildDescription: SPMBuildCore.ProductBuildDescription // setting is the package-level right now. We might need to figure out a better // answer for libraries if/when we support specifying deployment target at the // target-level. - args += try self.buildParameters.targetTripleArgs(for: self.product.targets[self.product.targets.startIndex]) + args += try self.buildParameters.tripleArgs(for: self.product.targets[self.product.targets.startIndex]) // Add arguments from declared build settings. args += self.buildSettingsFlags @@ -346,7 +346,7 @@ public final class ProductBuildDescription: SPMBuildCore.ProductBuildDescription // Library search path for the toolchain's copy of SwiftSyntax. #if BUILD_MACROS_AS_DYLIBS if product.type == .macro { - args += try ["-L", buildParameters.toolchain.hostLibDir.pathString] + args += try ["-L", defaultBuildParameters.toolchain.hostLibDir.pathString] } #endif diff --git a/Sources/Build/BuildDescription/ResolvedTarget+BuildDescription.swift b/Sources/Build/BuildDescription/ResolvedTarget+BuildDescription.swift new file mode 100644 index 00000000000..10024bd0d10 --- /dev/null +++ b/Sources/Build/BuildDescription/ResolvedTarget+BuildDescription.swift @@ -0,0 +1,23 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2015-2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import struct Basics.AbsolutePath +import struct PackageGraph.ResolvedTarget + +@_spi(SwiftPMInternal) +import SPMBuildCore + +extension ResolvedTarget { + func tempsPath(_ buildParameters: BuildParameters) -> AbsolutePath { + buildParameters.buildPath.appending(component: self.c99name + "\(self.buildTriple.suffix).build") + } +} diff --git a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift index a7216c99aa0..9edafd7bb9c 100644 --- a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift @@ -15,6 +15,8 @@ import Foundation import PackageGraph import PackageLoading import PackageModel + +@_spi(SwiftPMInternal) import SPMBuildCore #if USE_IMPL_ONLY_IMPORTS @@ -40,8 +42,11 @@ public final class SwiftTargetBuildDescription { /// a target is built. public let toolsVersion: ToolsVersion - /// The build parameters. - let buildParameters: BuildParameters + /// The build parameters for this target. + let defaultBuildParameters: BuildParameters + + /// The build parameters for build tools. + let toolsBuildParameters: BuildParameters /// Path to the temporary directory for this target. let tempsPath: AbsolutePath @@ -60,7 +65,7 @@ public final class SwiftTargetBuildDescription { /// Path to the bundle generated for this module (if any). var bundlePath: AbsolutePath? { if let bundleName = target.underlying.potentialBundleName, needsResourceBundle { - return self.buildParameters.bundlePath(named: bundleName) + return self.defaultBuildParameters.bundlePath(named: bundleName) } else { return .none } @@ -95,7 +100,7 @@ public final class SwiftTargetBuildDescription { let relativeSources = self.target.sources.relativePaths + self.derivedSources.relativePaths + self.pluginDerivedSources.relativePaths - let ltoEnabled = self.buildParameters.linkingParameters.linkTimeOptimizationMode != nil + let ltoEnabled = self.defaultBuildParameters.linkingParameters.linkTimeOptimizationMode != nil let objectFileExtension = ltoEnabled ? "bc" : "o" return try relativeSources.map { try AbsolutePath( @@ -106,16 +111,16 @@ public final class SwiftTargetBuildDescription { } var modulesPath: AbsolutePath { - return self.buildParameters.buildPath.appending(component: "Modules") + return self.defaultBuildParameters.buildPath.appending(component: "Modules\(self.target.buildTriple.suffix)") } /// The path to the swiftmodule file after compilation. public var moduleOutputPath: AbsolutePath { // note: needs to be public because of sourcekit-lsp // If we're an executable and we're not allowing test targets to link against us, we hide the module. - let triple = buildParameters.triple + let triple = defaultBuildParameters.triple let allowLinkingAgainstExecutables = (triple.isDarwin() || triple.isLinux() || triple.isWindows()) && self.toolsVersion >= .v5_5 let dirPath = (target.type == .executable && !allowLinkingAgainstExecutables) ? self.tempsPath : self.modulesPath - return dirPath.appending(component: self.target.c99name + ".swiftmodule") + return dirPath.appending(component: "\(self.target.c99name).swiftmodule") } /// The path to the wrapped swift module which is created using the modulewrap tool. This is required @@ -241,7 +246,7 @@ public final class SwiftTargetBuildDescription { private let shouldGenerateTestObservation: Bool /// Whether to disable sandboxing (e.g. for macros). - private let disableSandbox: Bool + private let shouldDisableSandbox: Bool /// Create a new target description with target and build parameters. init( @@ -249,13 +254,14 @@ public final class SwiftTargetBuildDescription { target: ResolvedTarget, toolsVersion: ToolsVersion, additionalFileRules: [FileRuleDescription] = [], - buildParameters: BuildParameters, + destinationBuildParameters: BuildParameters, + toolsBuildParameters: BuildParameters, buildToolPluginInvocationResults: [BuildToolPluginInvocationResult] = [], prebuildCommandResults: [PrebuildCommandResult] = [], requiredMacroProducts: [ResolvedProduct] = [], testTargetRole: TestTargetRole? = nil, shouldGenerateTestObservation: Bool = false, - disableSandbox: Bool, + shouldDisableSandbox: Bool, fileSystem: FileSystem, observabilityScope: ObservabilityScope ) throws { @@ -267,7 +273,9 @@ public final class SwiftTargetBuildDescription { self.package = package self.target = target self.toolsVersion = toolsVersion - self.buildParameters = buildParameters + self.defaultBuildParameters = destinationBuildParameters + self.toolsBuildParameters = toolsBuildParameters + // Unless mentioned explicitly, use the target type to determine if this is a test target. if let testTargetRole { self.testTargetRole = testTargetRole @@ -277,13 +285,13 @@ public final class SwiftTargetBuildDescription { self.testTargetRole = nil } - self.tempsPath = buildParameters.buildPath.appending(component: target.c99name + ".build") + self.tempsPath = target.tempsPath(destinationBuildParameters) self.derivedSources = Sources(paths: [], root: self.tempsPath.appending("DerivedSources")) self.buildToolPluginInvocationResults = buildToolPluginInvocationResults self.prebuildCommandResults = prebuildCommandResults self.requiredMacroProducts = requiredMacroProducts self.shouldGenerateTestObservation = shouldGenerateTestObservation - self.disableSandbox = disableSandbox + self.shouldDisableSandbox = shouldDisableSandbox self.fileSystem = fileSystem self.observabilityScope = observabilityScope @@ -291,7 +299,7 @@ public final class SwiftTargetBuildDescription { target: target, toolsVersion: toolsVersion, additionalFileRules: additionalFileRules, - buildParameters: buildParameters, + buildParameters: destinationBuildParameters, buildToolPluginInvocationResults: buildToolPluginInvocationResults, prebuildCommandResults: prebuildCommandResults, observabilityScope: observabilityScope @@ -328,18 +336,22 @@ public final class SwiftTargetBuildDescription { return } - guard self.buildParameters.triple.isDarwin(), self.buildParameters.testingParameters.experimentalTestOutput else { + guard + self.defaultBuildParameters.triple.isDarwin() && + self.defaultBuildParameters.testingParameters.experimentalTestOutput + else { return } - let content = generateTestObservationCode(buildParameters: self.buildParameters) + let content = generateTestObservationCode(buildParameters: self.defaultBuildParameters) // FIXME: We should generate this file during the actual build. self.derivedSources.relativePaths.append(subpath) try self.fileSystem.writeIfChanged(path: path, string: content) } - // FIXME: This will not work well for large files, as we will store the entire contents, plus its byte array representation in memory and also `writeIfChanged()` will read the entire generated file again. + // FIXME: This will not work well for large files, as we will store the entire contents, plus its byte array + // representation in memory and also `writeIfChanged()` will read the entire generated file again. private func generateResourceEmbeddingCode() throws { guard needsResourceEmbedding else { return } @@ -372,7 +384,7 @@ public final class SwiftTargetBuildDescription { guard let bundlePath else { return } let mainPathSubstitution: String - if self.buildParameters.triple.isWASI() { + if self.defaultBuildParameters.triple.isWASI() { // We prefer compile-time evaluation of the bundle path here for WASI. There's no benefit in evaluating this // at runtime, especially as `Bundle` support in WASI Foundation is partial. We expect all resource paths to // evaluate to `/\(resourceBundleName)/\(resourcePath)`, which allows us to pass this path to JS APIs like @@ -422,7 +434,11 @@ public final class SwiftTargetBuildDescription { private func packageNameArgumentIfSupported(with pkg: ResolvedPackage, packageAccess: Bool) -> [String] { let flag = "-package-name" if pkg.manifest.usePackageNameFlag, - DriverSupport.checkToolchainDriverFlags(flags: [flag], toolchain: self.buildParameters.toolchain, fileSystem: self.fileSystem) { + DriverSupport.checkToolchainDriverFlags( + flags: [flag], + toolchain: self.defaultBuildParameters.toolchain, + fileSystem: self.fileSystem + ) { if packageAccess { let pkgID = pkg.identity.description.spm_mangledToC99ExtendedIdentifier() return [flag, pkgID] @@ -436,12 +452,12 @@ public final class SwiftTargetBuildDescription { #if BUILD_MACROS_AS_DYLIBS self.requiredMacroProducts.forEach { macro in - args += ["-Xfrontend", "-load-plugin-library", "-Xfrontend", self.buildParameters.binaryPath(for: macro).pathString] + args += ["-Xfrontend", "-load-plugin-library", "-Xfrontend", self.toolsBuildParameters.binaryPath(for: macro).pathString] } #else try self.requiredMacroProducts.forEach { macro in if let macroTarget = macro.targets.first { - let executablePath = try self.buildParameters.binaryPath(for: macro).pathString + let executablePath = try self.toolsBuildParameters.binaryPath(for: macro).pathString args += ["-Xfrontend", "-load-plugin-executable", "-Xfrontend", "\(executablePath)#\(macroTarget.c99name)"] } else { throw InternalError("macro product \(macro.name) has no targets") // earlier validation should normally catch this @@ -450,7 +466,14 @@ public final class SwiftTargetBuildDescription { #endif // If we're using an OSS toolchain, add the required arguments bringing in the plugin server from the default toolchain if available. - if self.buildParameters.toolchain.isSwiftDevelopmentToolchain, DriverSupport.checkSupportedFrontendFlags(flags: ["-external-plugin-path"], toolchain: self.buildParameters.toolchain, fileSystem: self.fileSystem), let pluginServer = try self.buildParameters.toolchain.swiftPluginServerPath { + if self.defaultBuildParameters.toolchain.isSwiftDevelopmentToolchain, + DriverSupport.checkSupportedFrontendFlags( + flags: ["-external-plugin-path"], + toolchain: self.defaultBuildParameters.toolchain, + fileSystem: self.fileSystem + ), + let pluginServer = try self.defaultBuildParameters.toolchain.swiftPluginServerPath + { let toolchainUsrPath = pluginServer.parentDirectory.parentDirectory let pluginPathComponents = ["lib", "swift", "host", "plugins"] @@ -461,8 +484,12 @@ public final class SwiftTargetBuildDescription { args += ["-Xfrontend", "-external-plugin-path", "-Xfrontend", "\(localPluginPath)#\(pluginServer.pathString)"] } - if self.disableSandbox { - let toolchainSupportsDisablingSandbox = DriverSupport.checkSupportedFrontendFlags(flags: ["-disable-sandbox"], toolchain: self.buildParameters.toolchain, fileSystem: fileSystem) + if self.shouldDisableSandbox { + let toolchainSupportsDisablingSandbox = DriverSupport.checkSupportedFrontendFlags( + flags: ["-disable-sandbox"], + toolchain: self.defaultBuildParameters.toolchain, + fileSystem: fileSystem + ) if toolchainSupportsDisablingSandbox { args += ["-disable-sandbox"] } else { @@ -479,11 +506,11 @@ public final class SwiftTargetBuildDescription { /// The arguments needed to compile this target. public func compileArguments() throws -> [String] { var args = [String]() - args += try self.buildParameters.targetTripleArgs(for: self.target) + args += try self.defaultBuildParameters.tripleArgs(for: self.target) args += ["-swift-version", self.swiftVersion.rawValue] // pass `-v` during verbose builds. - if self.buildParameters.outputParameters.isVerbose { + if self.defaultBuildParameters.outputParameters.isVerbose { args += ["-v"] } @@ -491,22 +518,22 @@ public final class SwiftTargetBuildDescription { // // Technically, it should be enabled whenever WMO is off but we // don't currently make that distinction in SwiftPM - switch self.buildParameters.configuration { + switch self.defaultBuildParameters.configuration { case .debug: args += ["-enable-batch-mode"] case .release: break } - args += self.buildParameters.indexStoreArguments(for: self.target) + args += self.defaultBuildParameters.indexStoreArguments(for: self.target) args += self.optimizationArguments args += self.testingArguments - args += ["-j\(self.buildParameters.workers)"] + args += ["-j\(self.defaultBuildParameters.workers)"] args += self.activeCompilationConditions args += self.additionalFlags args += try self.moduleCacheArgs args += self.stdlibArguments - args += self.buildParameters.sanitizers.compileSwiftFlags() + args += self.defaultBuildParameters.sanitizers.compileSwiftFlags() args += ["-parseable-output"] // If we're compiling the main module of an executable other than the one that @@ -526,8 +553,8 @@ public final class SwiftTargetBuildDescription { // we can rename the symbol unconditionally. // No `-` for these flags because the set of Strings in driver.supportedFrontendFlags do // not have a leading `-` - if self.buildParameters.driverParameters.canRenameEntrypointFunctionName, - self.buildParameters.linkerFlagsForRenamingMainFunction(of: self.target) != nil + if self.defaultBuildParameters.driverParameters.canRenameEntrypointFunctionName, + self.defaultBuildParameters.linkerFlagsForRenamingMainFunction(of: self.target) != nil { args += ["-Xfrontend", "-entry-point-function-name", "-Xfrontend", "\(self.target.c99name)_main"] } @@ -540,7 +567,7 @@ public final class SwiftTargetBuildDescription { // Only add the build path to the framework search path if there are binary frameworks to link against. if !self.libraryBinaryPaths.isEmpty { - args += ["-F", self.buildParameters.buildPath.pathString] + args += ["-F", self.defaultBuildParameters.buildPath.pathString] } // Emit the ObjC compatibility header if enabled. @@ -549,12 +576,12 @@ public final class SwiftTargetBuildDescription { } // Add arguments needed for code coverage if it is enabled. - if self.buildParameters.testingParameters.enableCodeCoverage { + if self.defaultBuildParameters.testingParameters.enableCodeCoverage { args += ["-profile-coverage-mapping", "-profile-generate"] } // Add arguments to colorize output if stdout is tty - if self.buildParameters.outputParameters.isColorized { + if self.defaultBuildParameters.outputParameters.isColorized { args += ["-color-diagnostics"] } @@ -564,7 +591,7 @@ public final class SwiftTargetBuildDescription { switch testTargetRole { case .discovery: for dependency in try self.target.recursiveTargetDependencies() { - let dependencyScope = self.buildParameters.createScope(for: dependency) + let dependencyScope = self.defaultBuildParameters.createScope(for: dependency) let dependencySwiftFlags = dependencyScope.evaluate(.OTHER_SWIFT_FLAGS) if let interopModeFlag = dependencySwiftFlags.first(where: { $0.hasPrefix("-cxx-interoperability-mode=") }) { args += [interopModeFlag] @@ -584,17 +611,17 @@ public final class SwiftTargetBuildDescription { // Add the output for the `.swiftinterface`, if requested or if library evolution has been enabled some other // way. - if self.buildParameters.driverParameters.enableParseableModuleInterfaces || args.contains("-enable-library-evolution") { + if self.defaultBuildParameters.driverParameters.enableParseableModuleInterfaces || args.contains("-enable-library-evolution") { args += ["-emit-module-interface-path", self.parseableModuleInterfaceOutputPath.pathString] } - args += self.buildParameters.toolchain.extraFlags.swiftCompilerFlags + args += self.defaultBuildParameters.toolchain.extraFlags.swiftCompilerFlags // User arguments (from -Xswiftc) should follow generated arguments to allow user overrides - args += self.buildParameters.flags.swiftCompilerFlags + args += self.defaultBuildParameters.flags.swiftCompilerFlags - args += self.buildParameters.toolchain.extraFlags.cCompilerFlags.asSwiftcCCompilerFlags() + args += self.defaultBuildParameters.toolchain.extraFlags.cCompilerFlags.asSwiftcCCompilerFlags() // User arguments (from -Xcc) should follow generated arguments to allow user overrides - args += self.buildParameters.flags.cCompilerFlags.asSwiftcCCompilerFlags() + args += self.defaultBuildParameters.flags.cCompilerFlags.asSwiftcCCompilerFlags() // TODO: Pass -Xcxx flags to swiftc (#6491) // Uncomment when downstream support arrives. @@ -603,7 +630,7 @@ public final class SwiftTargetBuildDescription { // args += self.buildParameters.flags.cxxCompilerFlags.asSwiftcCXXCompilerFlags() // Enable the correct LTO mode if requested. - switch self.buildParameters.linkingParameters.linkTimeOptimizationMode { + switch self.defaultBuildParameters.linkingParameters.linkTimeOptimizationMode { case nil: break case .full: @@ -613,7 +640,7 @@ public final class SwiftTargetBuildDescription { } // Pass default include paths from the toolchain. - for includeSearchPath in self.buildParameters.toolchain.includeSearchPaths { + for includeSearchPath in self.defaultBuildParameters.toolchain.includeSearchPaths { args += ["-I", includeSearchPath.pathString] } @@ -637,7 +664,7 @@ public final class SwiftTargetBuildDescription { // rdar://117578677 // Pass -fno-omit-frame-pointer to support backtraces // this can be removed once the backtracer uses DWARF instead of frame pointers - if let omitFramePointers = self.buildParameters.debuggingParameters.omitFramePointers { + if let omitFramePointers = self.defaultBuildParameters.debuggingParameters.omitFramePointers { if omitFramePointers { args += ["-Xcc", "-fomit-frame-pointer"] } else { @@ -652,7 +679,7 @@ public final class SwiftTargetBuildDescription { /// such as emitting a module or supplementary outputs. public func emitCommandLine(scanInvocation: Bool = false) throws -> [String] { var result: [String] = [] - result.append(self.buildParameters.toolchain.swiftCompilerPath.pathString) + result.append(self.defaultBuildParameters.toolchain.swiftCompilerPath.pathString) result.append("-module-name") result.append(self.target.c99name) @@ -670,7 +697,7 @@ public final class SwiftTargetBuildDescription { result.append(try self.writeOutputFileMap().pathString) } - if self.buildParameters.useWholeModuleOptimization { + if self.defaultBuildParameters.useWholeModuleOptimization { result.append("-whole-module-optimization") result.append("-num-threads") result.append(String(ProcessInfo.processInfo.activeProcessorCount)) @@ -690,7 +717,7 @@ public final class SwiftTargetBuildDescription { /// Returns true if ObjC compatibility header should be emitted. private var shouldEmitObjCCompatibilityHeader: Bool { - self.buildParameters.triple.isDarwin() && self.target.type == .library + self.defaultBuildParameters.triple.isDarwin() && self.target.type == .library } func writeOutputFileMap() throws -> AbsolutePath { @@ -704,7 +731,7 @@ public final class SwiftTargetBuildDescription { """# - if self.buildParameters.useWholeModuleOptimization { + if self.defaultBuildParameters.useWholeModuleOptimization { let moduleName = self.target.c99name content += #""" @@ -735,7 +762,7 @@ public final class SwiftTargetBuildDescription { // Write out the entries for each source file. let sources = self.sources let objects = try self.objects - let ltoEnabled = self.buildParameters.linkingParameters.linkTimeOptimizationMode != nil + let ltoEnabled = self.defaultBuildParameters.linkingParameters.linkTimeOptimizationMode != nil let objectKey = ltoEnabled ? "llvm-bc" : "object" for idx in 0.. [String] { - let scope = self.buildParameters.createScope(for: self.target) + let scope = self.defaultBuildParameters.createScope(for: self.target) var flags: [String] = [] // Swift defines. @@ -838,7 +865,7 @@ public final class SwiftTargetBuildDescription { // Include path for the toolchain's copy of SwiftSyntax. #if BUILD_MACROS_AS_DYLIBS if target.type == .macro { - flags += try ["-I", self.buildParameters.toolchain.hostLibDir.pathString] + flags += try ["-I", self.defaultBuildParameters.toolchain.hostLibDir.pathString] } #endif @@ -849,7 +876,7 @@ public final class SwiftTargetBuildDescription { private var activeCompilationConditions: [String] { var compilationConditions = ["-DSWIFT_PACKAGE"] - switch self.buildParameters.configuration { + switch self.defaultBuildParameters.configuration { case .debug: compilationConditions += ["-DDEBUG"] case .release: @@ -861,7 +888,7 @@ public final class SwiftTargetBuildDescription { /// Optimization arguments according to the build configuration. private var optimizationArguments: [String] { - switch self.buildParameters.configuration { + switch self.defaultBuildParameters.configuration { case .debug: return ["-Onone"] case .release: @@ -875,7 +902,7 @@ public final class SwiftTargetBuildDescription { // test targets must be built with -enable-testing // since its required for test discovery (the non objective-c reflection kind) return ["-enable-testing"] - } else if self.buildParameters.testingParameters.enableTestability { + } else if self.defaultBuildParameters.testingParameters.enableTestability { return ["-enable-testing"] } else { return [] @@ -885,20 +912,20 @@ public final class SwiftTargetBuildDescription { /// Module cache arguments. private var moduleCacheArgs: [String] { get throws { - ["-module-cache-path", try self.buildParameters.moduleCache.pathString] + ["-module-cache-path", try self.defaultBuildParameters.moduleCache.pathString] } } private var stdlibArguments: [String] { var arguments: [String] = [] - let isLinkingStaticStdlib = self.buildParameters.linkingParameters.shouldLinkStaticSwiftStdlib - && self.buildParameters.triple.isSupportingStaticStdlib + let isLinkingStaticStdlib = self.defaultBuildParameters.linkingParameters.shouldLinkStaticSwiftStdlib + && self.defaultBuildParameters.triple.isSupportingStaticStdlib if isLinkingStaticStdlib { arguments += ["-static-stdlib"] } - if let resourcesPath = self.buildParameters.toolchain.swiftResourcesPath(isStatic: isLinkingStaticStdlib) { + if let resourcesPath = self.defaultBuildParameters.toolchain.swiftResourcesPath(isStatic: isLinkingStaticStdlib) { arguments += ["-resource-dir", "\(resourcesPath)"] } diff --git a/Sources/Build/BuildDescription/TargetBuildDescription.swift b/Sources/Build/BuildDescription/TargetBuildDescription.swift index 4fae9198680..ad9de913c2d 100644 --- a/Sources/Build/BuildDescription/TargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/TargetBuildDescription.swift @@ -101,7 +101,7 @@ public enum TargetBuildDescription { var buildParameters: BuildParameters { switch self { case .swift(let swiftTargetBuildDescription): - return swiftTargetBuildDescription.buildParameters + return swiftTargetBuildDescription.defaultBuildParameters case .clang(let clangTargetBuildDescription): return clangTargetBuildDescription.buildParameters } diff --git a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift index a51bf24d954..116467633e5 100644 --- a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift +++ b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift @@ -45,7 +45,7 @@ extension LLBuildManifestBuilder { let moduleNode = Node.file(target.moduleOutputPath) let cmdOutputs = objectNodes + [moduleNode] - if target.buildParameters.driverParameters.useIntegratedSwiftDriver { + if target.defaultBuildParameters.driverParameters.useIntegratedSwiftDriver { try self.addSwiftCmdsViaIntegratedDriver( target, inputs: inputs, @@ -68,7 +68,7 @@ extension LLBuildManifestBuilder { // jobs needed to build this Swift target. var commandLine = try target.emitCommandLine() commandLine.append("-driver-use-frontend-path") - commandLine.append(target.buildParameters.toolchain.swiftCompilerPath.pathString) + commandLine.append(target.defaultBuildParameters.toolchain.swiftCompilerPath.pathString) // FIXME: At some point SwiftPM should provide its own executor for // running jobs/launching processes during planning let resolver = try ArgsResolver(fileSystem: target.fileSystem) @@ -132,7 +132,7 @@ extension LLBuildManifestBuilder { // common intermediate dependency modules, such dependencies can lead // to cycles in the resulting manifest. var manifestNodeInputs: [Node] = [] - if targetDescription.buildParameters.driverParameters.useExplicitModuleBuild && !isMainModule(job) { + if targetDescription.defaultBuildParameters.driverParameters.useExplicitModuleBuild && !isMainModule(job) { manifestNodeInputs = jobInputs } else { manifestNodeInputs = (inputs + jobInputs).uniqued() @@ -192,7 +192,9 @@ extension LLBuildManifestBuilder { // Sort the product targets in topological order in order to collect and "bubble up" // their respective dependency graphs to the depending targets. let nodes: [ResolvedTarget.Dependency] = try self.plan.targetMap.keys.compactMap { - guard let target = self.plan.graph.allTargets[$0] else { throw InternalError("unknown target \($0)") } + guard let target = self.plan.graph.allTargets[$0] else { + throw InternalError("unknown target \($0)") + } return ResolvedTarget.Dependency.target(target, conditions: []) } let allPackageDependencies = try topologicalSort(nodes, successors: { $0.dependencies }) @@ -285,7 +287,7 @@ extension LLBuildManifestBuilder { // jobs needed to build this Swift target. var commandLine = try targetDescription.emitCommandLine() commandLine.append("-driver-use-frontend-path") - commandLine.append(targetDescription.buildParameters.toolchain.swiftCompilerPath.pathString) + commandLine.append(targetDescription.defaultBuildParameters.toolchain.swiftCompilerPath.pathString) commandLine.append("-experimental-explicit-module-build") let resolver = try ArgsResolver(fileSystem: self.fileSystem) let executor = SPMSwiftDriverExecutor( @@ -376,14 +378,14 @@ extension LLBuildManifestBuilder { cmdOutputs: [Node] ) throws { let isLibrary = target.target.type == .library || target.target.type == .test - let cmdName = target.target.getCommandName(config: target.buildParameters.buildConfig) + let cmdName = target.target.getCommandName(config: target.defaultBuildParameters.buildConfig) self.manifest.addWriteSourcesFileListCommand(sources: target.sources, sourcesFileListPath: target.sourcesFileListPath) self.manifest.addSwiftCmd( name: cmdName, inputs: inputs + [Node.file(target.sourcesFileListPath)], outputs: cmdOutputs, - executable: target.buildParameters.toolchain.swiftCompilerPath, + executable: target.defaultBuildParameters.toolchain.swiftCompilerPath, moduleName: target.target.c99name, moduleAliases: target.target.moduleAliases, moduleOutputPath: target.moduleOutputPath, @@ -394,7 +396,7 @@ extension LLBuildManifestBuilder { sources: target.sources, fileList: target.sourcesFileListPath, isLibrary: isLibrary, - wholeModuleOptimization: target.buildParameters.configuration == .release, + wholeModuleOptimization: target.defaultBuildParameters.configuration == .release, outputFileMapPath: try target.writeOutputFileMap() // FIXME: Eliminate side effect. ) } @@ -404,7 +406,7 @@ extension LLBuildManifestBuilder { ) throws -> [Node] { var inputs = target.sources.map(Node.file) - let swiftVersionFilePath = addSwiftGetVersionCommand(buildParameters: target.buildParameters) + let swiftVersionFilePath = addSwiftGetVersionCommand(buildParameters: target.defaultBuildParameters) inputs.append(.file(swiftVersionFilePath)) // Add resources node as the input to the target. This isn't great because we @@ -450,7 +452,7 @@ extension LLBuildManifestBuilder { } } - for dependency in target.target.dependencies(satisfying: target.buildParameters.buildEnvironment) { + for dependency in target.target.dependencies(satisfying: target.defaultBuildParameters.buildEnvironment) { switch dependency { case .target(let target, _): try addStaticTargetInputs(target) @@ -477,7 +479,7 @@ extension LLBuildManifestBuilder { } for binaryPath in target.libraryBinaryPaths { - let path = target.buildParameters.destinationPath(forBinaryAt: binaryPath) + let path = target.defaultBuildParameters.destinationPath(forBinaryAt: binaryPath) if self.fileSystem.isDirectory(binaryPath) { inputs.append(directory: path) } else { @@ -489,7 +491,7 @@ extension LLBuildManifestBuilder { // Depend on any required macro product's output. try target.requiredMacroProducts.forEach { macro in - try inputs.append(.virtual(macro.getLLBuildTargetName(config: target.buildParameters.buildConfig))) + try inputs.append(.virtual(macro.getLLBuildTargetName(config: target.defaultBuildParameters.buildConfig))) } return inputs + additionalInputs @@ -498,7 +500,7 @@ extension LLBuildManifestBuilder { /// Adds a top-level phony command that builds the entire target. private func addTargetCmd(_ target: SwiftTargetBuildDescription, cmdOutputs: [Node]) { // Create a phony node to represent the entire target. - let targetName = target.target.getLLBuildTargetName(config: target.buildParameters.buildConfig) + let targetName = target.target.getLLBuildTargetName(config: target.defaultBuildParameters.buildConfig) let targetOutput: Node = .virtual(targetName) self.manifest.addNode(targetOutput, toTarget: targetName) @@ -507,7 +509,7 @@ extension LLBuildManifestBuilder { inputs: cmdOutputs, outputs: [targetOutput] ) - if self.plan.graph.isInRootPackages(target.target, satisfying: target.buildParameters.buildEnvironment) { + if self.plan.graph.isInRootPackages(target.target, satisfying: target.defaultBuildParameters.buildEnvironment) { if !target.isTestTarget { self.addNode(targetOutput, toTarget: .main) } @@ -517,13 +519,13 @@ extension LLBuildManifestBuilder { private func addModuleWrapCmd(_ target: SwiftTargetBuildDescription) throws { // Add commands to perform the module wrapping Swift modules when debugging strategy is `modulewrap`. - guard target.buildParameters.debuggingStrategy == .modulewrap else { return } + guard target.defaultBuildParameters.debuggingStrategy == .modulewrap else { return } var moduleWrapArgs = [ - target.buildParameters.toolchain.swiftCompilerPath.pathString, + target.defaultBuildParameters.toolchain.swiftCompilerPath.pathString, "-modulewrap", target.moduleOutputPath.pathString, "-o", target.wrappedModuleOutputPath.pathString, ] - moduleWrapArgs += try target.buildParameters.targetTripleArgs(for: target.target) + moduleWrapArgs += try target.defaultBuildParameters.tripleArgs(for: target.target) self.manifest.addShellCmd( name: target.wrappedModuleOutputPath.pathString, description: "Wrapping AST for \(target.target.name) for debugging", diff --git a/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift b/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift index 4d9b6d62787..63038ce6a8a 100644 --- a/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift +++ b/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift @@ -14,6 +14,8 @@ import Basics import LLBuildManifest import PackageGraph import PackageModel + +@_spi(SwiftPMInternal) import SPMBuildCore #if USE_IMPL_ONLY_IMPORTS @@ -322,26 +324,26 @@ extension ResolvedTarget { } public func getLLBuildTargetName(config: String) -> String { - "\(name)-\(config).module" + "\(self.name)-\(config)\(self.buildTriple.suffix).module" } public func getLLBuildResourcesCmdName(config: String) -> String { - "\(name)-\(config).module-resources" + "\(self.name)-\(config).module-resources" } } extension ResolvedProduct { public func getLLBuildTargetName(config: String) throws -> String { - let potentialExecutableTargetName = "\(name)-\(config).exe" - let potentialLibraryTargetName = "\(name)-\(config).dylib" + let potentialExecutableTargetName = "\(name)-\(config)\(self.buildTriple.suffix).exe" + let potentialLibraryTargetName = "\(name)-\(config)\(self.buildTriple.suffix).dylib" switch type { case .library(.dynamic): return potentialLibraryTargetName case .test: - return "\(name)-\(config).test" + return "\(name)-\(config)\(self.buildTriple.suffix).test" case .library(.static): - return "\(name)-\(config).a" + return "\(name)-\(config)\(self.buildTriple.suffix).a" case .library(.automatic): throw InternalError("automatic library not supported") case .executable, .snippet: @@ -358,7 +360,7 @@ extension ResolvedProduct { } public func getCommandName(config: String) throws -> String { - try "C." + self.getLLBuildTargetName(config: config) + try "C.\(self.getLLBuildTargetName(config: config))\(self.buildTriple.suffix)" } } diff --git a/Sources/Build/BuildOperation.swift b/Sources/Build/BuildOperation.swift index fab2429cccf..d661ea9fdd5 100644 --- a/Sources/Build/BuildOperation.swift +++ b/Sources/Build/BuildOperation.swift @@ -13,7 +13,10 @@ @_spi(SwiftPMInternal) import Basics import LLBuildManifest + +@_spi(SwiftPMInternal) import PackageGraph + import PackageLoading import PackageModel import SPMBuildCore @@ -269,7 +272,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS } // TODO: Currently this function will only match frameworks. - internal func detectUnexpressedDependencies( + func detectUnexpressedDependencies( availableLibraries: [LibraryMetadata], targetDependencyMap: [String: [String]]? ) { @@ -295,7 +298,9 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS } let usedSDKDependencies: [String] = Set(possibleTempsPaths).flatMap { possibleTempsPath in - guard let contents = try? self.fileSystem.readFileContents(possibleTempsPath.appending(component: "\(c99name).d")) else { + guard let contents = try? self.fileSystem.readFileContents( + possibleTempsPath.appending(component: "\(c99name).d") + ) else { return [String]() } @@ -538,17 +543,20 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS // Invoke any build tool plugins in the graph to generate prebuild commands and build commands. if let pluginConfiguration, !self.productsBuildParameters.shouldSkipBuilding { // Hacky workaround for rdar://120560817, but it replicates precisely enough the original behavior before - // products/tools build parameters were split. Ideally we want to have specify the correct path at the time + // products/tools build parameters were split. Ideally we want to specify the correct path at the time // when `toolsBuildParameters` is initialized, but we have too many places in the codebase where that's // done, which makes it hard to realign them all at once. var pluginsBuildParameters = self.toolsBuildParameters pluginsBuildParameters.dataPath = pluginsBuildParameters.dataPath.parentDirectory.appending(components: ["plugins", "tools"]) + var buildToolsGraph = graph + try buildToolsGraph.updateBuildTripleRecursively(.tools) + let buildOperationForPluginDependencies = BuildOperation( // FIXME: this doesn't maintain the products/tools split cleanly productsBuildParameters: pluginsBuildParameters, toolsBuildParameters: pluginsBuildParameters, cacheBuildManifest: false, - packageGraphLoader: { return graph }, + packageGraphLoader: { buildToolsGraph }, additionalFileRules: self.additionalFileRules, pkgConfigDirectories: self.pkgConfigDirectories, dependenciesByRootPackageIdentity: [:], @@ -558,7 +566,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS fileSystem: self.fileSystem, observabilityScope: self.observabilityScope ) - buildToolPluginInvocationResults = try graph.invokeBuildToolPlugins( + buildToolPluginInvocationResults = try buildToolsGraph.invokeBuildToolPlugins( outputDir: pluginConfiguration.workDirectory.appending("outputs"), buildParameters: pluginsBuildParameters, additionalFileRules: self.additionalFileRules, @@ -576,7 +584,6 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS } } - // Surface any diagnostics from build tool plugins. var succeeded = true for (_, (target, results)) in buildToolPluginInvocationResults { @@ -656,7 +663,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS // Create the build plan based, on the graph and any information from plugins. let plan = try BuildPlan( - productsBuildParameters: self.productsBuildParameters, + destinationBuildParameters: self.productsBuildParameters, toolsBuildParameters: self.toolsBuildParameters, graph: graph, additionalFileRules: additionalFileRules, diff --git a/Sources/Build/BuildPlan/BuildPlan+Product.swift b/Sources/Build/BuildPlan/BuildPlan+Product.swift index d14ec2418ad..2cbe8a8c46d 100644 --- a/Sources/Build/BuildPlan/BuildPlan+Product.swift +++ b/Sources/Build/BuildPlan/BuildPlan+Product.swift @@ -70,7 +70,7 @@ extension BuildPlan { switch target.underlying { case is SwiftTarget: // Swift targets are guaranteed to have a corresponding Swift description. - guard case .swift(let description) = targetMap[target.id] else { + guard case .swift(let description) = self.targetMap[target.id] else { throw InternalError("unknown target \(target)") } @@ -220,9 +220,11 @@ extension BuildPlan { if product.targets.contains(id: target.id) { staticTargets.append(target) } - // Library targets should always be included. + // Library targets should always be included for the same build triple. case .library: - staticTargets.append(target) + if target.buildTriple == product.buildTriple { + staticTargets.append(target) + } // Add system target to system targets array. case .systemModule: systemModules.append(target) diff --git a/Sources/Build/BuildPlan/BuildPlan+Swift.swift b/Sources/Build/BuildPlan/BuildPlan+Swift.swift index 36b1cacde0c..058631598f2 100644 --- a/Sources/Build/BuildPlan/BuildPlan+Swift.swift +++ b/Sources/Build/BuildPlan/BuildPlan+Swift.swift @@ -19,7 +19,7 @@ extension BuildPlan { func plan(swiftTarget: SwiftTargetBuildDescription) throws { // We need to iterate recursive dependencies because Swift compiler needs to see all the targets a target // depends on. - let environment = swiftTarget.buildParameters.buildEnvironment + let environment = swiftTarget.defaultBuildParameters.buildEnvironment for case .target(let dependency, _) in try swiftTarget.target.recursiveDependencies(satisfying: environment) { switch dependency.underlying { case let underlyingTarget as ClangTarget where underlyingTarget.type == .library: @@ -40,7 +40,7 @@ extension BuildPlan { swiftTarget.additionalFlags += try pkgConfig(for: target).cFlags case let target as BinaryTarget: if case .xcframework = target.kind { - let libraries = try self.parseXCFramework(for: target, triple: swiftTarget.buildParameters.triple) + let libraries = try self.parseXCFramework(for: target, triple: swiftTarget.defaultBuildParameters.triple) for library in libraries { library.headersPaths.forEach { swiftTarget.additionalFlags += ["-I", $0.pathString, "-Xcc", "-I", "-Xcc", $0.pathString] diff --git a/Sources/Build/BuildPlan/BuildPlan+Test.swift b/Sources/Build/BuildPlan/BuildPlan+Test.swift index 93fd9da29c7..9d11d012d6e 100644 --- a/Sources/Build/BuildPlan/BuildPlan+Test.swift +++ b/Sources/Build/BuildPlan/BuildPlan+Test.swift @@ -26,15 +26,16 @@ import protocol TSCBasic.FileSystem extension BuildPlan { static func makeDerivedTestTargets( - _ buildParameters: BuildParameters, + destinationBuildParameters: BuildParameters, + toolsBuildParameters: BuildParameters, _ graph: PackageGraph, - _ disableSandbox: Bool, + shouldDisableSandbox: Bool, _ fileSystem: FileSystem, _ observabilityScope: ObservabilityScope ) throws -> [(product: ResolvedProduct, discoveryTargetBuildDescription: SwiftTargetBuildDescription?, entryPointTargetBuildDescription: SwiftTargetBuildDescription)] { - guard buildParameters.testingParameters.testProductStyle.requiresAdditionalDerivedTestTargets, - case .entryPointExecutable(let explicitlyEnabledDiscovery, let explicitlySpecifiedPath) = - buildParameters.testingParameters.testProductStyle + guard destinationBuildParameters.testingParameters.testProductStyle.requiresAdditionalDerivedTestTargets, + case .entryPointExecutable(let explicitlyEnabledDiscovery, let explicitlySpecifiedPath) = + destinationBuildParameters.testingParameters.testProductStyle else { throw InternalError("makeTestManifestTargets should not be used for build plan which does not require additional derived test targets") } @@ -68,7 +69,7 @@ extension BuildPlan { /// Generates test discovery targets, which contain derived sources listing the discovered tests. func generateDiscoveryTargets() throws -> (target: SwiftTarget, resolved: ResolvedTarget, buildDescription: SwiftTargetBuildDescription) { let discoveryTargetName = "\(package.manifest.displayName)PackageDiscoveredTests" - let discoveryDerivedDir = buildParameters.buildPath.appending(components: "\(discoveryTargetName).derived") + let discoveryDerivedDir = destinationBuildParameters.buildPath.appending(components: "\(discoveryTargetName).derived") let discoveryMainFile = discoveryDerivedDir.appending(component: TestDiscoveryTool.mainFileName) var discoveryPaths: [AbsolutePath] = [] @@ -96,9 +97,10 @@ extension BuildPlan { package: package, target: discoveryResolvedTarget, toolsVersion: toolsVersion, - buildParameters: buildParameters, + destinationBuildParameters: destinationBuildParameters, + toolsBuildParameters: toolsBuildParameters, testTargetRole: .discovery, - disableSandbox: disableSandbox, + shouldDisableSandbox: shouldDisableSandbox, fileSystem: fileSystem, observabilityScope: observabilityScope ) @@ -112,8 +114,8 @@ extension BuildPlan { swiftTargetDependencies: [Target.Dependency], resolvedTargetDependencies: [ResolvedTarget.Dependency] ) throws -> SwiftTargetBuildDescription { - let entryPointDerivedDir = buildParameters.buildPath.appending(components: "\(testProduct.name).derived") - let entryPointMainFileName = TestEntryPointTool.mainFileName(for: buildParameters.testingParameters.library) + let entryPointDerivedDir = destinationBuildParameters.buildPath.appending(components: "\(testProduct.name).derived") + let entryPointMainFileName = TestEntryPointTool.mainFileName(for: destinationBuildParameters.testingParameters.library) let entryPointMainFile = entryPointDerivedDir.appending(component: entryPointMainFileName) let entryPointSources = Sources(paths: [entryPointMainFile], root: entryPointDerivedDir) @@ -136,9 +138,10 @@ extension BuildPlan { package: package, target: entryPointResolvedTarget, toolsVersion: toolsVersion, - buildParameters: buildParameters, + destinationBuildParameters: destinationBuildParameters, + toolsBuildParameters: toolsBuildParameters, testTargetRole: .entryPoint(isSynthesized: true), - disableSandbox: disableSandbox, + shouldDisableSandbox: shouldDisableSandbox, fileSystem: fileSystem, observabilityScope: observabilityScope ) @@ -148,7 +151,7 @@ extension BuildPlan { let swiftTargetDependencies: [Target.Dependency] let resolvedTargetDependencies: [ResolvedTarget.Dependency] - switch buildParameters.testingParameters.library { + switch destinationBuildParameters.testingParameters.library { case .xctest: discoveryTargets = try generateDiscoveryTargets() swiftTargetDependencies = [.target(discoveryTargets!.target, conditions: [])] @@ -181,9 +184,10 @@ extension BuildPlan { package: package, target: entryPointResolvedTarget, toolsVersion: toolsVersion, - buildParameters: buildParameters, + destinationBuildParameters: destinationBuildParameters, + toolsBuildParameters: toolsBuildParameters, testTargetRole: .entryPoint(isSynthesized: false), - disableSandbox: disableSandbox, + shouldDisableSandbox: shouldDisableSandbox, fileSystem: fileSystem, observabilityScope: observabilityScope ) @@ -203,9 +207,10 @@ extension BuildPlan { package: package, target: entryPointResolvedTarget, toolsVersion: toolsVersion, - buildParameters: buildParameters, + destinationBuildParameters: destinationBuildParameters, + toolsBuildParameters: toolsBuildParameters, testTargetRole: .entryPoint(isSynthesized: false), - disableSandbox: disableSandbox, + shouldDisableSandbox: shouldDisableSandbox, fileSystem: fileSystem, observabilityScope: observabilityScope ) diff --git a/Sources/Build/BuildPlan/BuildPlan.swift b/Sources/Build/BuildPlan/BuildPlan.swift index 1f4eda79e5f..2c4c7519fd8 100644 --- a/Sources/Build/BuildPlan/BuildPlan.swift +++ b/Sources/Build/BuildPlan/BuildPlan.swift @@ -127,13 +127,13 @@ extension BuildParameters { return [] } - /// Computes the target triple arguments for a given resolved target. - public func targetTripleArgs(for target: ResolvedTarget) throws -> [String] { + public func tripleArgs(for target: ResolvedTarget) throws -> [String] { + // confusingly enough this is the triple argument, not the target argument var args = ["-target"] // Compute the triple string for Darwin platform using the platform version. if self.triple.isDarwin() { - let platform = buildEnvironment.platform + let platform = self.buildEnvironment.platform let supportedPlatform = target.getSupportedPlatform(for: platform, usingXCTest: target.type == .test) args += [self.triple.tripleString(forPlatformVersion: supportedPlatform.version.versionString)] } else { @@ -158,7 +158,7 @@ extension BuildParameters { /// Returns the scoped view of build settings for a given target. func createScope(for target: ResolvedTarget) -> BuildSettings.Scope { - return BuildSettings.Scope(target.underlying.buildSettings, environment: buildEnvironment) + BuildSettings.Scope(target.underlying.buildSettings, environment: buildEnvironment) } } @@ -185,16 +185,6 @@ public class BuildPlan: SPMBuildCore.BuildPlan { /// Build parameters used for tools. public let toolsBuildParameters: BuildParameters - /// Triple for which this target is compiled. - private func buildTriple(for target: ResolvedTarget) -> Basics.Triple { - self.buildParameters(for: target).triple - } - - /// Triple for which this product is compiled. - private func buildTriple(for product: ResolvedProduct) -> Basics.Triple { - self.buildParameters(for: product).triple - } - /// The package graph. public let graph: PackageGraph @@ -231,14 +221,14 @@ public class BuildPlan: SPMBuildCore.BuildPlan { /// Cache for pkgConfig flags. private var pkgConfigCache = [SystemLibraryTarget: (cFlags: [String], libs: [String])]() - /// Cache for library information. + /// Cache for library information. private var externalLibrariesCache = [BinaryTarget: [LibraryInfo]]() - /// Cache for tools information. + /// Cache for tools information. var externalExecutablesCache = [BinaryTarget: [ExecutableInfo]]() /// Whether to disable sandboxing (e.g. for macros). - private let disableSandbox: Bool + private let shouldDisableSandbox: Bool /// The filesystem to operate on. let fileSystem: any FileSystem @@ -246,7 +236,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan { /// ObservabilityScope with which to emit diagnostics let observabilityScope: ObservabilityScope - @available(*, deprecated, renamed: "init(productsBuildParameters:toolsBuildParameters:graph:)") + @available(*, deprecated, renamed: "init(destinationBuildParameters:toolsBuildParameters:graph:)") public convenience init( buildParameters: BuildParameters, graph: PackageGraph, @@ -257,7 +247,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan { observabilityScope: ObservabilityScope ) throws { try self.init( - productsBuildParameters: buildParameters, + destinationBuildParameters: buildParameters, toolsBuildParameters: buildParameters, graph: graph, additionalFileRules: additionalFileRules, @@ -268,11 +258,29 @@ public class BuildPlan: SPMBuildCore.BuildPlan { ) } - /// Create a build plan with a package graph and explicitly distinct build parameters for products and tools. - public init( + @available(*, deprecated, renamed: "init(destinationBuildParameters:toolsBuildParameters:graph:fileSystem:observabilityScope:)") + public convenience init( productsBuildParameters: BuildParameters, toolsBuildParameters: BuildParameters, graph: PackageGraph, + fileSystem: any FileSystem, + observabilityScope: ObservabilityScope + ) throws { + try self.init( + destinationBuildParameters: productsBuildParameters, + toolsBuildParameters: toolsBuildParameters, + graph: graph, + fileSystem: fileSystem, + observabilityScope: observabilityScope + ) + } + + /// Create a build plan with a package graph and explicitly distinct build parameters for destination platform and + /// tools platform. + public init( + destinationBuildParameters: BuildParameters, + toolsBuildParameters: BuildParameters, + graph: PackageGraph, additionalFileRules: [FileRuleDescription] = [], buildToolPluginInvocationResults: [ResolvedTarget.ID: [BuildToolPluginInvocationResult]] = [:], prebuildCommandResults: [ResolvedTarget.ID: [PrebuildCommandResult]] = [:], @@ -280,16 +288,17 @@ public class BuildPlan: SPMBuildCore.BuildPlan { fileSystem: any FileSystem, observabilityScope: ObservabilityScope ) throws { - self.destinationBuildParameters = productsBuildParameters + self.destinationBuildParameters = destinationBuildParameters self.toolsBuildParameters = toolsBuildParameters self.graph = graph self.buildToolPluginInvocationResults = buildToolPluginInvocationResults self.prebuildCommandResults = prebuildCommandResults - self.disableSandbox = disableSandbox + self.shouldDisableSandbox = disableSandbox self.fileSystem = fileSystem self.observabilityScope = observabilityScope.makeChildScope(description: "Build Plan") - var productMap: [ResolvedProduct.ID: (product: ResolvedProduct, buildDescription: ProductBuildDescription)] = [:] + var productMap: [ResolvedProduct.ID: (product: ResolvedProduct, buildDescription: ProductBuildDescription)] = + [:] // Create product description for each product we have in the package graph that is eligible. for product in graph.allProducts where product.shouldCreateProductDescription { let buildParameters: BuildParameters @@ -297,7 +306,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan { case .tools: buildParameters = toolsBuildParameters case .destination: - buildParameters = productsBuildParameters + buildParameters = destinationBuildParameters } guard let package = graph.package(for: product) else { @@ -335,7 +344,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan { case .tools: buildParameters = toolsBuildParameters case .destination: - buildParameters = productsBuildParameters + buildParameters = destinationBuildParameters } // Validate the product dependencies of this target. @@ -384,12 +393,13 @@ public class BuildPlan: SPMBuildCore.BuildPlan { target: target, toolsVersion: toolsVersion, additionalFileRules: additionalFileRules, - buildParameters: buildParameters, + destinationBuildParameters: buildParameters, + toolsBuildParameters: toolsBuildParameters, buildToolPluginInvocationResults: buildToolPluginInvocationResults[target.id] ?? [], prebuildCommandResults: prebuildCommandResults[target.id] ?? [], requiredMacroProducts: requiredMacroProducts, shouldGenerateTestObservation: generateTestObservation, - disableSandbox: self.disableSandbox, + shouldDisableSandbox: self.shouldDisableSandbox, fileSystem: fileSystem, observabilityScope: observabilityScope ) @@ -436,18 +446,21 @@ public class BuildPlan: SPMBuildCore.BuildPlan { } // Plan the derived test targets, if necessary. - if productsBuildParameters.testingParameters.testProductStyle.requiresAdditionalDerivedTestTargets { + if destinationBuildParameters.testingParameters.testProductStyle.requiresAdditionalDerivedTestTargets { let derivedTestTargets = try Self.makeDerivedTestTargets( - productsBuildParameters, + destinationBuildParameters: destinationBuildParameters, + toolsBuildParameters: toolsBuildParameters, graph, - self.disableSandbox, + shouldDisableSandbox: self.shouldDisableSandbox, self.fileSystem, self.observabilityScope ) for item in derivedTestTargets { var derivedTestTargets = [item.entryPointTargetBuildDescription.target] - targetMap[item.entryPointTargetBuildDescription.target.id] = .swift(item.entryPointTargetBuildDescription) + targetMap[item.entryPointTargetBuildDescription.target.id] = .swift( + item.entryPointTargetBuildDescription + ) if let discoveryTargetBuildDescription = item.discoveryTargetBuildDescription { targetMap[discoveryTargetBuildDescription.target.id] = .swift(discoveryTargetBuildDescription) @@ -553,9 +566,9 @@ public class BuildPlan: SPMBuildCore.BuildPlan { } // Add search paths from the system library targets. - for target in graph.reachableTargets { + for target in self.graph.reachableTargets { if let systemLib = target.underlying as? SystemLibraryTarget { - arguments.append(contentsOf: try self.pkgConfig(for: systemLib).cFlags) + try arguments.append(contentsOf: self.pkgConfig(for: systemLib).cFlags) // Add the path to the module map. arguments += ["-I", systemLib.moduleMapPath.parentDirectory.pathString] } @@ -590,7 +603,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan { } // Add search paths from the system library targets. - for target in graph.reachableTargets { + for target in self.graph.reachableTargets { if let systemLib = target.underlying as? SystemLibraryTarget { arguments += try self.pkgConfig(for: systemLib).cFlags } @@ -729,7 +742,7 @@ extension ResolvedProduct { } private var isBinaryOnly: Bool { - return self.targets.filter({ !($0.underlying is BinaryTarget) }).isEmpty + self.targets.filter { !($0.underlying is BinaryTarget) }.isEmpty } private var isPlugin: Bool { diff --git a/Sources/Build/CMakeLists.txt b/Sources/Build/CMakeLists.txt index fcc51aed76d..d978c3ff53a 100644 --- a/Sources/Build/CMakeLists.txt +++ b/Sources/Build/CMakeLists.txt @@ -10,6 +10,7 @@ add_library(Build BuildDescription/ClangTargetBuildDescription.swift BuildDescription/PluginDescription.swift BuildDescription/ProductBuildDescription.swift + BuildDescription/ResolvedTarget+BuildDescription.swift BuildDescription/SwiftTargetBuildDescription.swift BuildDescription/TargetBuildDescription.swift BuildManifest/LLBuildManifestBuilder.swift diff --git a/Sources/Commands/PackageTools/PluginCommand.swift b/Sources/Commands/PackageTools/PluginCommand.swift index 1968dae8183..c90553d7b1c 100644 --- a/Sources/Commands/PackageTools/PluginCommand.swift +++ b/Sources/Commands/PackageTools/PluginCommand.swift @@ -14,7 +14,10 @@ import ArgumentParser import Basics import CoreCommands import Dispatch + +@_spi(SwiftPMInternal) import PackageGraph + import PackageModel import enum TSCBasic.ProcessEnv @@ -315,6 +318,9 @@ struct PluginCommand: SwiftCommand { let toolSearchDirs = [try swiftTool.getTargetToolchain().swiftCompilerPath.parentDirectory] + getEnvSearchPaths(pathString: ProcessEnv.path, currentWorkingDirectory: .none) + var buildToolsGraph = packageGraph + try buildToolsGraph.updateBuildTripleRecursively(.tools) + let buildParameters = try swiftTool.toolsBuildParameters // Build or bring up-to-date any executable host-side tools on which this plugin depends. Add them and any binary dependencies to the tool-names-to-path map. let buildSystem = try swiftTool.createBuildSystem( @@ -323,10 +329,12 @@ struct PluginCommand: SwiftCommand { // Force all dependencies to be built for the host, to work around the fact that BuildOperation.plan // knows to compile build tool plugin dependencies for the host but does not do the same for command // plugins. - productsBuildParameters: buildParameters + productsBuildParameters: buildParameters, + packageGraphLoader: { buildToolsGraph } ) + let accessibleTools = try plugin.processAccessibleTools( - packageGraph: packageGraph, + packageGraph: buildToolsGraph, fileSystem: swiftTool.fileSystem, environment: buildParameters.buildEnvironment, for: try pluginScriptRunner.hostTriple diff --git a/Sources/Commands/SwiftTestTool.swift b/Sources/Commands/SwiftTestTool.swift index 1a713702f43..63e8fde2305 100644 --- a/Sources/Commands/SwiftTestTool.swift +++ b/Sources/Commands/SwiftTestTool.swift @@ -421,7 +421,7 @@ public struct SwiftTestTool: SwiftCommand { let toolchain = try swiftTool.getTargetToolchain() let testEnv = try TestingSupport.constructTestEnvironment( toolchain: toolchain, - buildParameters: buildParameters, + destinationBuildParameters: buildParameters, sanitizers: globalOptions.build.sanitizers ) @@ -662,13 +662,17 @@ extension SwiftTestTool { // MARK: - swift-testing private func swiftTestingRun(_ swiftTool: SwiftTool) throws { - let buildParameters = try swiftTool.buildParametersForTest(enableCodeCoverage: false, shouldSkipBuilding: sharedOptions.shouldSkipBuilding, library: .swiftTesting) + let buildParameters = try swiftTool.buildParametersForTest( + enableCodeCoverage: false, + shouldSkipBuilding: sharedOptions.shouldSkipBuilding, + library: .swiftTesting + ) let testProducts = try buildTestsIfNeeded(swiftTool: swiftTool, buildParameters: buildParameters) let toolchain = try swiftTool.getTargetToolchain() let testEnv = try TestingSupport.constructTestEnvironment( toolchain: toolchain, - buildParameters: buildParameters, + destinationBuildParameters: buildParameters, sanitizers: globalOptions.build.sanitizers ) @@ -970,7 +974,7 @@ final class ParallelTestRunner { let testEnv = try TestingSupport.constructTestEnvironment( toolchain: self.toolchain, - buildParameters: self.buildParameters, + destinationBuildParameters: self.buildParameters, sanitizers: self.buildOptions.sanitizers ) diff --git a/Sources/Commands/Utilities/PluginDelegate.swift b/Sources/Commands/Utilities/PluginDelegate.swift index d4ad1554444..ec1257fcece 100644 --- a/Sources/Commands/Utilities/PluginDelegate.swift +++ b/Sources/Commands/Utilities/PluginDelegate.swift @@ -235,7 +235,7 @@ final class PluginDelegate: PluginInvocationDelegate { // Construct the environment we'll pass down to the tests. let testEnvironment = try TestingSupport.constructTestEnvironment( toolchain: toolchain, - buildParameters: toolsBuildParameters, + destinationBuildParameters: toolsBuildParameters, sanitizers: swiftTool.options.build.sanitizers ) diff --git a/Sources/Commands/Utilities/SymbolGraphExtract.swift b/Sources/Commands/Utilities/SymbolGraphExtract.swift index f9423384c9b..7bdb96dd907 100644 --- a/Sources/Commands/Utilities/SymbolGraphExtract.swift +++ b/Sources/Commands/Utilities/SymbolGraphExtract.swift @@ -66,7 +66,7 @@ public struct SymbolGraphExtract { // Construct arguments for extracting symbols for a single target. var commandLine = [self.tool.pathString] commandLine += ["-module-name", target.c99name] - commandLine += try buildParameters.targetTripleArgs(for: target) + commandLine += try buildParameters.tripleArgs(for: target) commandLine += try buildPlan.createAPIToolCommonArgs(includeLibrarySearchPaths: true) commandLine += ["-module-cache-path", try buildParameters.moduleCache.pathString] if verboseOutput { diff --git a/Sources/Commands/Utilities/TestingSupport.swift b/Sources/Commands/Utilities/TestingSupport.swift index 236feaa072e..7556a3e0c56 100644 --- a/Sources/Commands/Utilities/TestingSupport.swift +++ b/Sources/Commands/Utilities/TestingSupport.swift @@ -36,7 +36,9 @@ enum TestingSupport { func findXCTestHelper(swiftBuildPath: AbsolutePath) -> AbsolutePath? { // XCTestHelper tool is installed in libexec. - let maybePath = swiftBuildPath.parentDirectory.parentDirectory.appending(components: "libexec", "swift", "pm", "swiftpm-xctest-helper") + let maybePath = swiftBuildPath.parentDirectory.parentDirectory.appending( + components: "libexec", "swift", "pm", "swiftpm-xctest-helper" + ) if swiftTool.fileSystem.isFile(maybePath) { return maybePath } else { @@ -46,7 +48,10 @@ enum TestingSupport { } if let firstCLIArgument = CommandLine.arguments.first { - let runningSwiftBuildPath = try AbsolutePath(validating: firstCLIArgument, relativeTo: swiftTool.originalWorkingDirectory) + let runningSwiftBuildPath = try AbsolutePath( + validating: firstCLIArgument, + relativeTo: swiftTool.originalWorkingDirectory + ) if let xctestHelperPath = findXCTestHelper(swiftBuildPath: runningSwiftBuildPath) { return xctestHelperPath } @@ -54,7 +59,10 @@ enum TestingSupport { // This will be true during swiftpm development or when using swift.org toolchains. let xcodePath = try TSCBasic.Process.checkNonZeroExit(args: "/usr/bin/xcode-select", "--print-path").spm_chomp() - let installedSwiftBuildPath = try TSCBasic.Process.checkNonZeroExit(args: "/usr/bin/xcrun", "--find", "swift-build", environment: ["DEVELOPER_DIR": xcodePath]).spm_chomp() + let installedSwiftBuildPath = try TSCBasic.Process.checkNonZeroExit( + args: "/usr/bin/xcrun", "--find", "swift-build", + environment: ["DEVELOPER_DIR": xcodePath] + ).spm_chomp() if let xctestHelperPath = findXCTestHelper(swiftBuildPath: try AbsolutePath(validating: installedSwiftBuildPath)) { return xctestHelperPath } @@ -110,7 +118,7 @@ enum TestingSupport { args = [try Self.xctestHelperPath(swiftTool: swiftTool).pathString, path.pathString, tempFile.path.pathString] let env = try Self.constructTestEnvironment( toolchain: try swiftTool.getTargetToolchain(), - buildParameters: swiftTool.buildParametersForTest( + destinationBuildParameters: swiftTool.buildParametersForTest( enableCodeCoverage: enableCodeCoverage, shouldSkipBuilding: shouldSkipBuilding, experimentalTestOutput: experimentalTestOutput, @@ -126,7 +134,7 @@ enum TestingSupport { #else let env = try Self.constructTestEnvironment( toolchain: try swiftTool.getTargetToolchain(), - buildParameters: swiftTool.buildParametersForTest( + destinationBuildParameters: swiftTool.buildParametersForTest( enableCodeCoverage: enableCodeCoverage, shouldSkipBuilding: shouldSkipBuilding, library: .xctest @@ -143,7 +151,7 @@ enum TestingSupport { /// Creates the environment needed to test related tools. static func constructTestEnvironment( toolchain: UserToolchain, - buildParameters: BuildParameters, + destinationBuildParameters buildParameters: BuildParameters, sanitizers: [Sanitizer] ) throws -> EnvironmentVariables { var env = EnvironmentVariables.process() diff --git a/Sources/Commands/Utilities/XCTEvents.swift b/Sources/Commands/Utilities/XCTEvents.swift index a264b205e3a..0ceedfce77f 100644 --- a/Sources/Commands/Utilities/XCTEvents.swift +++ b/Sources/Commands/Utilities/XCTEvents.swift @@ -237,12 +237,12 @@ extension TestErrorInfo { extension TestIssue { init(_ issue: XCTIssue) { self.init( - type: .init(destinationBuildParameters: issue.type), + type: .init(defaultBuildParameters: issue.type), compactDescription: issue.compactDescription, detailedDescription: issue.detailedDescription, - associatedError: issue.associatedError.map { .init(destinationBuildParameters: $0) }, - sourceCodeContext: .init(destinationBuildParameters: issue.sourceCodeContext), - attachments: issue.attachments.map { .init(destinationBuildParameters: $0) } + associatedError: issue.associatedError.map { .init(defaultBuildParameters: $0) }, + sourceCodeContext: .init(defaultBuildParameters: issue.sourceCodeContext), + attachments: issue.attachments.map { .init(defaultBuildParameters: $0) } ) } } @@ -275,8 +275,8 @@ extension TestLocation { extension TestSourceCodeContext { init(_ context: XCTSourceCodeContext) { self.init( - callStack: context.callStack.map { .init(destinationBuildParameters: $0) }, - location: context.location.map { .init(destinationBuildParameters: $0) } + callStack: context.callStack.map { .init(defaultBuildParameters: $0) }, + location: context.location.map { .init(defaultBuildParameters: $0) } ) } } @@ -285,8 +285,8 @@ extension TestSourceCodeFrame { init(_ frame: XCTSourceCodeFrame) { self.init( address: frame.address, - symbolInfo: (try? frame.symbolInfo()).map { .init(destinationBuildParameters: $0) }, - symbolicationError: frame.symbolicationError.map { .init(destinationBuildParameters: $0) } + symbolInfo: (try? frame.symbolInfo()).map { .init(defaultBuildParameters: $0) }, + symbolicationError: frame.symbolicationError.map { .init(defaultBuildParameters: $0) } ) } } @@ -296,7 +296,7 @@ extension TestSourceCodeSymbolInfo { self.init( imageName: symbolInfo.imageName, symbolName: symbolInfo.symbolName, - location: symbolInfo.location.map { .init(destinationBuildParameters: $0) } + location: symbolInfo.location.map { .init(defaultBuildParameters: $0) } ) } } diff --git a/Sources/PackageGraph/BuildTriple.swift b/Sources/PackageGraph/BuildTriple.swift index 4e121a2c7bb..87d2daf21f1 100644 --- a/Sources/PackageGraph/BuildTriple.swift +++ b/Sources/PackageGraph/BuildTriple.swift @@ -10,6 +10,9 @@ // //===----------------------------------------------------------------------===// +import class PackageModel.Target +import class PackageModel.Product + /// Triple for which code should be compiled for. /// > Note: We're not using "host" and "target" triple terminology in this enum, as that clashes with build /// > system "targets" and can lead to confusion in this context. @@ -20,3 +23,23 @@ public enum BuildTriple { /// Triple of the destination platform for which end products are compiled (the target triple). case destination } + +extension Target { + var buildTriple: BuildTriple { + if self.type == .macro || self.type == .plugin { + .tools + } else { + .destination + } + } +} + +extension Product { + var buildTriple: BuildTriple { + if self.type == .macro || self.type == .plugin { + .tools + } else { + .destination + } + } +} diff --git a/Sources/PackageGraph/PackageGraph+Loading.swift b/Sources/PackageGraph/PackageGraph+Loading.swift index 420e1ccce67..7bd38f7ab78 100644 --- a/Sources/PackageGraph/PackageGraph+Loading.swift +++ b/Sources/PackageGraph/PackageGraph+Loading.swift @@ -165,12 +165,12 @@ extension PackageGraph { observabilityScope: observabilityScope ) - let rootPackages = resolvedPackages.filter{ root.manifests.values.contains($0.manifest) } + let rootPackages = resolvedPackages.filter { root.manifests.values.contains($0.manifest) } checkAllDependenciesAreUsed(rootPackages, observabilityScope: observabilityScope) return try PackageGraph( rootPackages: rootPackages, - rootDependencies: resolvedPackages.filter{ rootDependencies.contains($0.manifest) }, + rootDependencies: resolvedPackages.filter { rootDependencies.contains($0.manifest) }, dependencies: requiredDependencies, binaryArtifacts: binaryArtifacts ) @@ -180,16 +180,16 @@ extension PackageGraph { private func checkAllDependenciesAreUsed(_ rootPackages: [ResolvedPackage], observabilityScope: ObservabilityScope) { for package in rootPackages { // List all dependency products dependent on by the package targets. - let productDependencies = IdentifiableSet(package.targets.flatMap({ target in - return target.dependencies.compactMap({ targetDependency in + let productDependencies = IdentifiableSet(package.targets.flatMap { target in + return target.dependencies.compactMap { targetDependency in switch targetDependency { case .product(let product, _): return product case .target: return nil } - }) - })) + } + }) for dependency in package.dependencies { // We continue if the dependency contains executable products to make sure we don't @@ -217,7 +217,12 @@ private func checkAllDependenciesAreUsed(_ rootPackages: [ResolvedPackage], obse ) // Otherwise emit a warning if none of the dependency package's products are used. - let dependencyIsUsed = dependency.products.contains(where: { productDependencies.contains(id: $0.id) }) + let dependencyIsUsed = dependency.products.contains { product in + // Don't compare by product ID, but by product name to make sure both build triples as properties of + // `ResolvedProduct.ID` are allowed. + productDependencies.contains { $0.name == product.name } + } + if !dependencyIsUsed && !observabilityScope.errorsReportedInAnyScope { packageDiagnosticsScope.emit(.unusedDependency(dependency.identity.description)) } @@ -627,12 +632,12 @@ private func createResolvedPackages( observabilityScope.emit( ModuleError.duplicateModule( targetName: entry.key, - packages: entry.value.map{ $0.identity }) + packages: entry.value.map { $0.identity }) ) } } - return try packageBuilders.map{ try $0.construct() } + return try packageBuilders.map { try $0.construct() } } private func emitDuplicateProductDiagnostic( diff --git a/Sources/PackageGraph/PackageGraph.swift b/Sources/PackageGraph/PackageGraph.swift index bd3446b0499..6ca5fc68b59 100644 --- a/Sources/PackageGraph/PackageGraph.swift +++ b/Sources/PackageGraph/PackageGraph.swift @@ -59,17 +59,16 @@ public struct PackageGraph { public let packages: [ResolvedPackage] /// The list of all targets reachable from root targets. - public let reachableTargets: IdentifiableSet + public private(set) var reachableTargets: IdentifiableSet /// The list of all products reachable from root targets. - public let reachableProducts: IdentifiableSet + public private(set) var reachableProducts: IdentifiableSet /// Returns all the targets in the graph, regardless if they are reachable from the root targets or not. - public let allTargets: IdentifiableSet + public private(set) var allTargets: IdentifiableSet /// Returns all the products in the graph, regardless if they are reachable from the root targets or not. - - public let allProducts: IdentifiableSet + public private(set) var allProducts: IdentifiableSet /// Package dependencies required for a fully resolved graph. /// @@ -101,14 +100,14 @@ public struct PackageGraph { return self.rootPackages.contains(id: package.id) } - private let targetsToPackages: [ResolvedTarget.ID: ResolvedPackage] + private var targetsToPackages: [ResolvedTarget.ID: ResolvedPackage] /// Returns the package that contains the target, or nil if the target isn't in the graph. public func package(for target: ResolvedTarget) -> ResolvedPackage? { return self.targetsToPackages[target.id] } - private let productsToPackages: [ResolvedProduct.ID: ResolvedPackage] + private var productsToPackages: [ResolvedProduct.ID: ResolvedPackage] /// Returns the package that contains the product, or nil if the product isn't in the graph. public func package(for product: ResolvedProduct) -> ResolvedPackage? { return self.productsToPackages[product.id] @@ -132,44 +131,68 @@ public struct PackageGraph { self.inputPackages = rootPackages + rootDependencies self.binaryArtifacts = binaryArtifacts self.packages = try topologicalSort(inputPackages, successors: { $0.dependencies }) + let identitiesToPackages = self.packages.spm_createDictionary { ($0.identity, $0) } // Create a mapping from targets to the packages that define them. Here // we include all targets, including tests in non-root packages, since // this is intended for lookup and not traversal. - self.targetsToPackages = packages.reduce(into: [:], { partial, package in - package.targets.forEach{ partial[$0.id] = package } + var targetsToPackages = self.packages.reduce(into: [:], { partial, package in + package.targets.forEach { partial[$0.id] = package } + }) + + // Create a mapping from products to the packages that define them. Here + // we include all products, including tests in non-root packages, since + // this is intended for lookup and not traversal. + var productsToPackages = packages.reduce(into: [:], { partial, package in + package.products.forEach { partial[$0.id] = package } }) - let allTargets = IdentifiableSet(packages.flatMap({ package -> [ResolvedTarget] in + var allTargets = IdentifiableSet() + var allProducts = IdentifiableSet() + for package in self.packages { + let targetsToInclude: [ResolvedTarget] if rootPackages.contains(id: package.id) { - return package.targets + targetsToInclude = package.targets } else { // Don't include tests targets from non-root packages so swift-test doesn't // try to run them. - return package.targets.filter({ $0.type != .test }) + targetsToInclude = package.targets.filter { $0.type != .test } } - })) - // Create a mapping from products to the packages that define them. Here - // we include all products, including tests in non-root packages, since - // this is intended for lookup and not traversal. - self.productsToPackages = packages.reduce(into: [:], { partial, package in - package.products.forEach { partial[$0.id] = package } - }) + for target in targetsToInclude { + allTargets.insert(target) + + // Explicitly include dependencies of host tools in the maps of all targets or all products + if target.buildTriple == .tools { + for dependency in try target.recursiveDependencies() { + switch dependency { + case .target(let targetDependency, _): + allTargets.insert(targetDependency) + targetsToPackages[targetDependency.id] = package + case .product(let productDependency, _): + allProducts.insert(productDependency) + productsToPackages[productDependency.id] = + identitiesToPackages[productDependency.packageIdentity] + } + } + } + } - let allProducts = IdentifiableSet(packages.flatMap({ package -> [ResolvedProduct] in if rootPackages.contains(id: package.id) { - return package.products + allProducts.formUnion(package.products) } else { // Don't include tests products from non-root packages so swift-test doesn't // try to run them. - return package.products.filter({ $0.type != .test }) + allProducts.formUnion(package.products.filter { $0.type != .test }) } - })) + } + + self.targetsToPackages = targetsToPackages + self.productsToPackages = productsToPackages // Compute the reachable targets and products. - let inputTargets = inputPackages.flatMap { $0.targets } - let inputProducts = inputPackages.flatMap { $0.products } + let inputTargets = self.inputPackages.flatMap { $0.targets } + let inputProducts = self.inputPackages.flatMap { $0.products } let recursiveDependencies = try inputTargets.lazy.flatMap { try $0.recursiveDependencies() } self.reachableTargets = IdentifiableSet(inputTargets).union(recursiveDependencies.compactMap { $0.target }) @@ -179,6 +202,42 @@ public struct PackageGraph { self.allProducts = allProducts } + @_spi(SwiftPMInternal) + public mutating func updateBuildTripleRecursively(_ buildTriple: BuildTriple) throws { + self.reachableTargets = IdentifiableSet(self.reachableTargets.map { + var target = $0 + target.buildTriple = buildTriple + return target + }) + self.reachableProducts = IdentifiableSet(self.reachableProducts.map { + var product = $0 + product.buildTriple = buildTriple + return product + }) + + self.allTargets = IdentifiableSet(self.allTargets.map { + var target = $0 + target.buildTriple = buildTriple + return target + }) + self.allProducts = IdentifiableSet(self.allProducts.map { + var product = $0 + product.buildTriple = buildTriple + return product + }) + + self.targetsToPackages = .init(self.targetsToPackages.map { + var target = $0 + target.buildTriple = buildTriple + return (target, $1) + }, uniquingKeysWith: { $1 }) + self.productsToPackages = .init(self.productsToPackages.map { + var product = $0 + product.buildTriple = buildTriple + return (product, $1) + }, uniquingKeysWith: { $1 }) + } + /// Computes a map from each executable target in any of the root packages to the corresponding test targets. func computeTestTargetsForExecutableTargets() throws -> [ResolvedTarget.ID: [ResolvedTarget]] { var result = [ResolvedTarget.ID: [ResolvedTarget]]() diff --git a/Sources/PackageGraph/Resolution/ResolvedProduct.swift b/Sources/PackageGraph/Resolution/ResolvedProduct.swift index 1208b5b0d7f..b2d23135c2f 100644 --- a/Sources/PackageGraph/Resolution/ResolvedProduct.swift +++ b/Sources/PackageGraph/Resolution/ResolvedProduct.swift @@ -30,7 +30,7 @@ public struct ResolvedProduct { public let underlying: Product /// The top level targets contained in this product. - public let targets: IdentifiableSet + public private(set) var targets: IdentifiableSet /// Executable target for test entry point file. public let testEntryPointTarget: ResolvedTarget? @@ -44,7 +44,11 @@ public struct ResolvedProduct { public let platformVersionProvider: PlatformVersionProvider /// Triple for which this resolved product should be compiled for. - public let buildTriple: BuildTriple + public internal(set) var buildTriple: BuildTriple { + didSet { + self.updateBuildTriplesOfDependencies() + } + } /// The main executable target of product. /// @@ -63,7 +67,11 @@ public struct ResolvedProduct { } } - public init(packageIdentity: PackageIdentity, product: Product, targets: IdentifiableSet) { + public init( + packageIdentity: PackageIdentity, + product: Product, + targets: IdentifiableSet + ) { assert(product.targets.count == targets.count && product.targets.map(\.name).sorted() == targets.map(\.name).sorted()) self.packageIdentity = packageIdentity self.underlying = product @@ -97,7 +105,18 @@ public struct ResolvedProduct { ) } - self.buildTriple = .destination + self.buildTriple = product.buildTriple + self.updateBuildTriplesOfDependencies() + } + + private mutating func updateBuildTriplesOfDependencies() { + if case .tools = self.buildTriple { + self.targets = IdentifiableSet(self.targets.map { + var target = $0 + target.buildTriple = .tools + return target + }) + } } /// True if this product contains Swift targets. @@ -166,13 +185,13 @@ extension ResolvedProduct { extension ResolvedProduct: Identifiable { /// Resolved target identity that uniquely identifies it in a resolution graph. public struct ID: Hashable { - public let targetName: String + public let productName: String let packageIdentity: PackageIdentity - public let buildTriple: BuildTriple + public var buildTriple: BuildTriple } public var id: ID { - ID(targetName: self.name, packageIdentity: self.packageIdentity, buildTriple: self.buildTriple) + ID(productName: self.name, packageIdentity: self.packageIdentity, buildTriple: self.buildTriple) } } diff --git a/Sources/PackageGraph/Resolution/ResolvedTarget.swift b/Sources/PackageGraph/Resolution/ResolvedTarget.swift index 4e9fecaf804..674df077510 100644 --- a/Sources/PackageGraph/Resolution/ResolvedTarget.swift +++ b/Sources/PackageGraph/Resolution/ResolvedTarget.swift @@ -133,7 +133,7 @@ public struct ResolvedTarget { public let underlying: Target /// The dependencies of this target. - public let dependencies: [Dependency] + public private(set) var dependencies: [Dependency] /// The default localization for resources. public let defaultLocalization: String? @@ -144,7 +144,11 @@ public struct ResolvedTarget { private let platformVersionProvider: PlatformVersionProvider /// Triple for which this resolved target should be compiled for. - public let buildTriple: BuildTriple + public internal (set) var buildTriple: BuildTriple { + didSet { + self.updateBuildTriplesOfDependencies() + } + } /// Create a resolved target instance. public init( @@ -161,7 +165,26 @@ public struct ResolvedTarget { self.defaultLocalization = defaultLocalization self.supportedPlatforms = supportedPlatforms self.platformVersionProvider = platformVersionProvider - self.buildTriple = .destination + self.buildTriple = underlying.buildTriple + self.updateBuildTriplesOfDependencies() + } + + private mutating func updateBuildTriplesOfDependencies() { + if case .tools = self.buildTriple { + for (i, dependency) in dependencies.enumerated() { + let updatedDependency: Dependency + switch dependency { + case .target(var target, let conditions): + target.buildTriple = .tools + updatedDependency = .target(target, conditions: conditions) + case .product(var product, let conditions): + product.buildTriple = .tools + updatedDependency = .product(product, conditions: conditions) + } + + dependencies[i] = updatedDependency + } + } } public func getSupportedPlatform(for platform: Platform, usingXCTest: Bool) -> SupportedPlatform { @@ -175,7 +198,7 @@ public struct ResolvedTarget { extension ResolvedTarget: CustomStringConvertible { public var description: String { - return "" + return "" } } @@ -244,7 +267,7 @@ extension ResolvedTarget: Identifiable { public struct ID: Hashable { public let targetName: String let packageIdentity: PackageIdentity - public let buildTriple: BuildTriple + public var buildTriple: BuildTriple } public var id: ID { diff --git a/Sources/PackageModel/Target/Target.swift b/Sources/PackageModel/Target/Target.swift index 541e37794de..411415a719f 100644 --- a/Sources/PackageModel/Target/Target.swift +++ b/Sources/PackageModel/Target/Target.swift @@ -131,7 +131,7 @@ public class Target: PolymorphicCodableProtocol { /// The name of the target. /// /// NOTE: This name is not the language-level target (i.e., the importable - /// name) name in many cases, instead use c99name if you need uniqueness. + /// name) name in many cases, instead use ``Target/c99name`` if you need uniqueness. public private(set) var name: String /// Module aliases needed to build this target. The key is an original name of a diff --git a/Sources/SPMBuildCore/BuildParameters/BuildParameters.swift b/Sources/SPMBuildCore/BuildParameters/BuildParameters.swift index cbc5cb5499c..6e4ceb04aa2 100644 --- a/Sources/SPMBuildCore/BuildParameters/BuildParameters.swift +++ b/Sources/SPMBuildCore/BuildParameters/BuildParameters.swift @@ -248,7 +248,7 @@ public struct BuildParameters: Encodable { /// Returns the path to the binary of a product for the current build parameters, relative to the build directory. public func binaryRelativePath(for product: ResolvedProduct) throws -> RelativePath { - let potentialExecutablePath = try RelativePath(validating: "\(product.name)\(self.triple.executableExtension)") + let potentialExecutablePath = try RelativePath(validating: "\(product.name)\(product.buildTriple.suffix)\(self.triple.executableExtension)") switch product.type { case .executable, .snippet: @@ -329,3 +329,12 @@ extension Triple { return !self.isWindows() } } + +extension BuildTriple { + /// Suffix appended to build manifest nodes to distinguish nodes created for tools from nodes created for + /// end products, i.e. nodes for host vs target triples. + @_spi(SwiftPMInternal) + public var suffix: String { + if self == .tools { "-tool" } else { "" } + } +} diff --git a/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift b/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift index e8d1fd3efe1..5d9e1e0e982 100644 --- a/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift +++ b/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift @@ -74,7 +74,7 @@ extension ProductBuildDescription { /// The path to the product binary produced. public var binaryPath: AbsolutePath { get throws { - return try self.buildParameters.binaryPath(for: product) + try self.buildParameters.binaryPath(for: product) } } } diff --git a/Sources/SPMBuildCore/Plugins/PluginInvocation.swift b/Sources/SPMBuildCore/Plugins/PluginInvocation.swift index 72dcef42431..f948995f7e6 100644 --- a/Sources/SPMBuildCore/Plugins/PluginInvocation.swift +++ b/Sources/SPMBuildCore/Plugins/PluginInvocation.swift @@ -592,7 +592,9 @@ extension PackageGraph { } // Associate the list of results with the target. The list will have one entry for each plugin used by the target. - pluginResultsByTarget[target.id] = (target, buildToolPluginResults) + var targetID = target.id + targetID.buildTriple = .destination + pluginResultsByTarget[targetID] = (target, buildToolPluginResults) } return pluginResultsByTarget } diff --git a/Sources/SPMTestSupport/MockBuildTestHelper.swift b/Sources/SPMTestSupport/MockBuildTestHelper.swift index 2aeb77764ff..58297fdd979 100644 --- a/Sources/SPMTestSupport/MockBuildTestHelper.swift +++ b/Sources/SPMTestSupport/MockBuildTestHelper.swift @@ -15,6 +15,8 @@ import Basics @_spi(SwiftPMInternal) import Build +import struct PackageGraph.ResolvedTarget +import struct PackageGraph.ResolvedProduct import PackageModel import SPMBuildCore import TSCUtility @@ -73,14 +75,14 @@ public let defaultTargetTriple: String = hostTriple.tripleString #endif public func mockBuildParameters( - buildPath: AbsolutePath = "/path/to/build", + buildPath: AbsolutePath? = nil, config: BuildConfiguration = .debug, toolchain: PackageModel.Toolchain = MockToolchain(), flags: PackageModel.BuildFlags = PackageModel.BuildFlags(), shouldLinkStaticSwiftStdlib: Bool = false, shouldDisableLocalRpath: Bool = false, canRenameEntrypointFunctionName: Bool = false, - targetTriple: Basics.Triple = hostTriple, + triple: Basics.Triple = hostTriple, indexStoreMode: BuildParameters.IndexStoreMode = .off, useExplicitModuleBuild: Bool = false, linkerDeadStrip: Bool = true, @@ -88,16 +90,16 @@ public func mockBuildParameters( omitFramePointers: Bool? = nil ) -> BuildParameters { try! BuildParameters( - dataPath: buildPath, + dataPath: buildPath ?? AbsolutePath("/path/to/build").appending(triple.tripleString), configuration: config, toolchain: toolchain, - triple: targetTriple, + triple: triple, flags: flags, pkgConfigDirectories: [], workers: 3, indexStoreMode: indexStoreMode, debuggingParameters: .init( - triple: targetTriple, + triple: triple, shouldEnableDebuggingEntitlement: config == .debug, omitFramePointers: omitFramePointers ), @@ -129,7 +131,7 @@ public func mockBuildParameters(environment: BuildEnvironment) -> BuildParameter fatalError("unsupported platform in tests") } - return mockBuildParameters(config: environment.configuration ?? .debug, targetTriple: triple) + return mockBuildParameters(config: environment.configuration ?? .debug, triple: triple) } enum BuildError: Swift.Error { @@ -138,15 +140,15 @@ enum BuildError: Swift.Error { public struct BuildPlanResult { public let plan: Build.BuildPlan - public let targetMap: [String: TargetBuildDescription] - public let productMap: [String: Build.ProductBuildDescription] + public let targetMap: [ResolvedTarget.ID: TargetBuildDescription] + public let productMap: [ResolvedProduct.ID: Build.ProductBuildDescription] public init(plan: Build.BuildPlan) throws { self.plan = plan self.productMap = try Dictionary( throwingUniqueKeysWithValues: plan.buildProducts .compactMap { $0 as? Build.ProductBuildDescription } - .map { ($0.product.name, $0) } + .map { ($0.product.id, $0) } ) self.targetMap = try Dictionary( throwingUniqueKeysWithValues: plan.targetMap.compactMap { @@ -156,7 +158,7 @@ public struct BuildPlanResult { else { throw BuildError.error("Target \($0) not found.") } - return (target.name, $1) + return (target.id, $1) } ) } @@ -170,16 +172,26 @@ public struct BuildPlanResult { } public func target(for name: String) throws -> TargetBuildDescription { - guard let target = targetMap[name] else { - throw BuildError.error("Target \(name) not found.") + let matchingIDs = targetMap.keys.filter({ $0.targetName == name }) + guard matchingIDs.count == 1, let target = targetMap[matchingIDs[0]] else { + if matchingIDs.isEmpty { + throw BuildError.error("Target \(name) not found.") + } else { + throw BuildError.error("More than one target \(name) found.") + } } return target } public func buildProduct(for name: String) throws -> Build.ProductBuildDescription { - guard let product = productMap[name] else { - // Display the thrown error on macOS - throw BuildError.error("Product \(name) not found.") + let matchingIDs = productMap.keys.filter({ $0.productName == name }) + guard matchingIDs.count == 1, let product = productMap[matchingIDs[0]] else { + if matchingIDs.isEmpty { + // Display the thrown error on macOS + throw BuildError.error("Product \(name) not found.") + } else { + throw BuildError.error("More than one target \(name) found.") + } } return product } diff --git a/Sources/SPMTestSupport/MockPackageGraphs.swift b/Sources/SPMTestSupport/MockPackageGraphs.swift new file mode 100644 index 00000000000..b84b897a257 --- /dev/null +++ b/Sources/SPMTestSupport/MockPackageGraphs.swift @@ -0,0 +1,122 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import class Basics.ObservabilitySystem +import class Basics.ObservabilityScope +import struct PackageGraph.PackageGraph +import class PackageModel.Manifest +import struct PackageModel.ProductDescription +import struct PackageModel.TargetDescription +import protocol TSCBasic.FileSystem +import class TSCBasic.InMemoryFileSystem + +public func macrosPackageGraph() throws -> ( + graph: PackageGraph, + fileSystem: FileSystem, + observabilityScope: ObservabilityScope +) { + let fs = InMemoryFileSystem(emptyFiles: + "/swift-firmware/Sources/Core/source.swift", + "/swift-firmware/Sources/HAL/source.swift", + "/swift-firmware/Tests/CoreTests/source.swift", + "/swift-firmware/Tests/HALTests/source.swift", + "/swift-mmio/Sources/MMIO/source.swift", + "/swift-mmio/Sources/MMIOMacros/source.swift", + "/swift-syntax/Sources/SwiftSyntax/source.swift", + "/swift-syntax/Tests/SwiftSyntaxTests/source.swift" + ) + + let observability = ObservabilitySystem.makeForTesting() + let graph = try loadPackageGraph( + fileSystem: fs, + manifests: [ + Manifest.createRootManifest( + displayName: "swift-firmware", + path: "/swift-firmware", + dependencies: [ + .localSourceControl( + path: "/swift-mmio", + requirement: .upToNextMajor(from: "1.0.0") + ) + ], + products: [ + ProductDescription( + name: "Core", + type: .executable, + targets: ["Core"] + ) + ], + targets: [ + TargetDescription( + name: "Core", + dependencies: ["HAL"], + type: .executable + ), + TargetDescription( + name: "HAL", + dependencies: [.product(name: "MMIO", package: "swift-mmio")] + ), + TargetDescription(name: "CoreTests", dependencies: ["Core"], type: .test), + TargetDescription(name: "HALTests", dependencies: ["HAL"], type: .test), + ] + ), + Manifest.createFileSystemManifest( + displayName: "swift-mmio", + path: "/swift-mmio", + dependencies: [ + .localSourceControl( + path: "/swift-syntax", + requirement: .upToNextMajor(from: "1.0.0") + ) + ], + products: [ + ProductDescription( + name: "MMIO", + type: .library(.automatic), + targets: ["MMIO"] + ) + ], + targets: [ + TargetDescription( + name: "MMIO", + dependencies: [.target(name: "MMIOMacros")] + ), + TargetDescription( + name: "MMIOMacros", + dependencies: [.product(name: "SwiftSyntax", package: "swift-syntax")], + type: .macro + ) + ] + ), + Manifest.createFileSystemManifest( + displayName: "swift-syntax", + path: "/swift-syntax", + products: [ + ProductDescription( + name: "SwiftSyntax", + type: .library(.automatic), + targets: ["SwiftSyntax"] + ) + ], + targets: [ + TargetDescription(name: "SwiftSyntax", dependencies: []), + TargetDescription(name: "SwiftSyntaxTests", dependencies: ["SwiftSyntax"], type: .test), + ] + ), + ], + observabilityScope: observability.topScope + ) + + XCTAssertNoDiagnostics(observability.diagnostics) + + return (graph, fs, observability.topScope) +} diff --git a/Sources/SPMTestSupport/PackageGraphTester.swift b/Sources/SPMTestSupport/PackageGraphTester.swift index fc4d3c231b5..91d2d61d8e3 100644 --- a/Sources/SPMTestSupport/PackageGraphTester.swift +++ b/Sources/SPMTestSupport/PackageGraphTester.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2014-2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information @@ -98,6 +98,15 @@ public final class PackageGraphResult { body(ResolvedTargetResult(target)) } + public func checkTargets( + _ name: String, + file: StaticString = #file, + line: UInt = #line, + body: ([ResolvedTargetResult]) -> Void + ) { + body(graph.allTargets.filter { $0.name == name }.map(ResolvedTargetResult.init)) + } + public func checkProduct( _ name: String, file: StaticString = #file, @@ -149,7 +158,7 @@ public final class PackageGraphResult { } public final class ResolvedTargetResult { - private let target: ResolvedTarget + let target: ResolvedTarget init(_ target: ResolvedTarget) { self.target = target @@ -176,7 +185,9 @@ public final class ResolvedTargetResult { } public func checkDeclaredPlatforms(_ platforms: [String: String], file: StaticString = #file, line: UInt = #line) { - let targetPlatforms = Dictionary(uniqueKeysWithValues: target.supportedPlatforms.map({ ($0.platform.name, $0.version.versionString) })) + let targetPlatforms = Dictionary( + uniqueKeysWithValues: target.supportedPlatforms.map { ($0.platform.name, $0.version.versionString) } + ) XCTAssertEqual(platforms, targetPlatforms, file: file, line: line) } @@ -187,16 +198,24 @@ public final class ResolvedTargetResult { return self.target.getSupportedPlatform(for: platform, usingXCTest: self.target.type == .test) } let targetPlatforms = Dictionary( - uniqueKeysWithValues: derived - .map { ($0.platform.name, $0.version.versionString) } + uniqueKeysWithValues: derived.map { ($0.platform.name, $0.version.versionString) } ) XCTAssertEqual(platforms, targetPlatforms, file: file, line: line) } - public func checkDerivedPlatformOptions(_ platform: PackageModel.Platform, options: [String], file: StaticString = #file, line: UInt = #line) { - let platform = target.getSupportedPlatform(for: platform, usingXCTest: target.type == .test) + public func checkDerivedPlatformOptions( + _ platform: PackageModel.Platform, + options: [String], + file: StaticString = #file, + line: UInt = #line + ) { + let platform = self.target.getSupportedPlatform(for: platform, usingXCTest: target.type == .test) XCTAssertEqual(platform.options, options, file: file, line: line) } + + public func check(buildTriple: BuildTriple, file: StaticString = #file, line: UInt = #line) { + XCTAssertEqual(self.target.buildTriple, buildTriple, file: file, line: line) + } } public final class ResolvedTargetDependencyResult { @@ -217,6 +236,28 @@ public final class ResolvedTargetDependencyResult { ) { XCTAssert(!dependency.conditions.allSatisfy({ $0.satisfies(environment) }), file: file, line: line) } + + public func checkTarget( + file: StaticString = #file, + line: UInt = #line, + body: (ResolvedTargetResult) -> Void + ) { + guard case let .target(target, _) = self.dependency else { + return XCTFail("Dependency \(dependency) is not a target", file: file, line: line) + } + body(ResolvedTargetResult(target)) + } + + public func checkProduct( + file: StaticString = #file, + line: UInt = #line, + body: (ResolvedProductResult) -> Void + ) { + guard case let .product(product, _) = self.dependency else { + return XCTFail("Dependency \(dependency) is not a product", file: file, line: line) + } + body(ResolvedProductResult(product)) + } } public final class ResolvedProductResult { @@ -252,6 +293,22 @@ public final class ResolvedProductResult { let platform = product.getSupportedPlatform(for: platform, usingXCTest: product.isLinkingXCTest) XCTAssertEqual(platform.options, options, file: file, line: line) } + + public func check(buildTriple: BuildTriple, file: StaticString = #file, line: UInt = #line) { + XCTAssertEqual(self.product.buildTriple, buildTriple, file: file, line: line) + } + + public func checkTarget( + _ name: String, + file: StaticString = #file, + line: UInt = #line, + body: (ResolvedTargetResult) -> Void + ) { + guard let target = product.targets.first(where: { $0.name == name }) else { + return XCTFail("Target \(name) not found", file: file, line: line) + } + body(ResolvedTargetResult(target)) + } } extension ResolvedTarget.Dependency { diff --git a/Tests/BuildTests/BuildOperationTests.swift b/Tests/BuildTests/BuildOperationTests.swift index 3f196dd41fa..6a703ded853 100644 --- a/Tests/BuildTests/BuildOperationTests.swift +++ b/Tests/BuildTests/BuildOperationTests.swift @@ -22,14 +22,16 @@ import class TSCBasic.InMemoryFileSystem final class BuildOperationTests: XCTestCase { func testDetectUnexpressedDependencies() throws { + let buildParameters = mockBuildParameters(shouldDisableLocalRpath: false) + let fs = InMemoryFileSystem(files: [ - "/path/to/build/debug/Lunch.build/Lunch.d" : "/Best.framework" + "\(buildParameters.dataPath)/debug/Lunch.build/Lunch.d" : "/Best.framework" ]) let observability = ObservabilitySystem.makeForTesting() let buildOp = BuildOperation( - productsBuildParameters: mockBuildParameters(shouldDisableLocalRpath: false), - toolsBuildParameters: mockBuildParameters(shouldDisableLocalRpath: false), + productsBuildParameters: buildParameters, + toolsBuildParameters: buildParameters, cacheBuildManifest: false, packageGraphLoader: { fatalError() }, additionalFileRules: [], diff --git a/Tests/BuildTests/BuildPlanTests.swift b/Tests/BuildTests/BuildPlanTests.swift index 2055e433f4b..5ea4e286dac 100644 --- a/Tests/BuildTests/BuildPlanTests.swift +++ b/Tests/BuildTests/BuildPlanTests.swift @@ -47,7 +47,7 @@ extension Build.BuildPlan { observabilityScope: ObservabilityScope ) throws { try self.init( - productsBuildParameters: buildParameters, + destinationBuildParameters: buildParameters, toolsBuildParameters: buildParameters, graph: graph, additionalFileRules: additionalFileRules, @@ -888,7 +888,7 @@ final class BuildPlanTests: XCTestCase { buildPath: buildDirPath, config: .release, toolchain: UserToolchain.default, - targetTriple: UserToolchain.default.targetTriple, + triple: UserToolchain.default.targetTriple, useExplicitModuleBuild: true ), graph: graph, @@ -1118,11 +1118,11 @@ final class BuildPlanTests: XCTestCase { observabilityScope: observability.topScope )) - XCTAssertEqual(Set(result.productMap.keys), ["APackageTests"]) + XCTAssertEqual(Set(result.productMap.keys.map(\.productName)), ["APackageTests"]) #if os(macOS) - XCTAssertEqual(Set(result.targetMap.keys), ["ATarget", "BTarget", "ATargetTests"]) + XCTAssertEqual(Set(result.targetMap.keys.map(\.targetName)), ["ATarget", "BTarget", "ATargetTests"]) #else - XCTAssertEqual(Set(result.targetMap.keys), [ + XCTAssertEqual(Set(result.targetMap.keys.map(\.targetName)), [ "APackageTests", "APackageDiscoveredTests", "ATarget", @@ -1469,7 +1469,13 @@ final class BuildPlanTests: XCTestCase { ]) #endif - let buildProduct = try XCTUnwrap(result.productMap["exe"]) + let buildProduct = try XCTUnwrap( + result.productMap[.init( + productName: "exe", + packageIdentity: "Pkg", + buildTriple: .destination + )] + ) XCTAssertEqual(Array(buildProduct.objects), [ buildPath.appending(components: "exe.build", "main.c.o"), buildPath.appending(components: "extlib.build", "extlib.c.o"), @@ -1712,8 +1718,9 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) + let buildParameters = mockBuildParameters() let plan = try BuildPlan( - buildParameters: mockBuildParameters(), + buildParameters: buildParameters, graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -1784,7 +1791,7 @@ final class BuildPlanTests: XCTestCase { "@\(buildPath.appending(components: "exe.product", "Objects.LinkFileList"))", "-Xlinker", "-rpath", "-Xlinker", "/fake/path/lib/swift-5.5/macosx", "-target", defaultTargetTriple, - "-Xlinker", "-add_ast_path", "-Xlinker", "/path/to/build/debug/exe.build/exe.swiftmodule", + "-Xlinker", "-add_ast_path", "-Xlinker", "/path/to/build/\(buildParameters.triple)/debug/exe.build/exe.swiftmodule", "-g", ]) #elseif os(Windows) @@ -1840,8 +1847,9 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) + let buildParameters = mockBuildParameters() let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(), + buildParameters: buildParameters, graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -1851,8 +1859,8 @@ final class BuildPlanTests: XCTestCase { let lib = try result.target(for: "lib").clangTarget() XCTAssertEqual(try lib.objects, [ - AbsolutePath("/path/to/build/debug/lib.build/lib.S.o"), - AbsolutePath("/path/to/build/debug/lib.build/lib.c.o"), + AbsolutePath("/path/to/build/\(buildParameters.triple)/debug/lib.build/lib.S.o"), + AbsolutePath("/path/to/build/\(buildParameters.triple)/debug/lib.build/lib.c.o"), ]) } @@ -2567,7 +2575,7 @@ final class BuildPlanTests: XCTestCase { // Verify that `-lstdc++` is passed instead of `-lc++` when cross-compiling to Linux. result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(targetTriple: .arm64Linux), + buildParameters: mockBuildParameters(triple: .arm64Linux), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -3373,7 +3381,7 @@ final class BuildPlanTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(targetTriple: .windows), + buildParameters: mockBuildParameters(triple: .windows), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -3460,7 +3468,7 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - var parameters = mockBuildParameters(targetTriple: .wasi) + var parameters = mockBuildParameters(triple: .wasi) parameters.linkingParameters.shouldLinkStaticSwiftStdlib = true let result = try BuildPlanResult(plan: BuildPlan( buildParameters: parameters, @@ -3562,7 +3570,7 @@ final class BuildPlanTests: XCTestCase { try BuildPlanResult(plan: BuildPlan( buildParameters: mockBuildParameters( canRenameEntrypointFunctionName: true, - targetTriple: triple + triple: triple ), graph: graph, fileSystem: fs, @@ -3759,7 +3767,7 @@ final class BuildPlanTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(targetTriple: .init("arm64-apple-ios")), + buildParameters: mockBuildParameters(triple: .init("arm64-apple-ios")), graph: graph, fileSystem: fileSystem, observabilityScope: observability.topScope @@ -3836,7 +3844,7 @@ final class BuildPlanTests: XCTestCase { // constraints above are valid. XCTAssertNoThrow( _ = try BuildPlan( - buildParameters: mockBuildParameters(targetTriple: .arm64iOS), + buildParameters: mockBuildParameters(triple: .arm64iOS), graph: graph, fileSystem: fileSystem, observabilityScope: observability.topScope @@ -3846,7 +3854,7 @@ final class BuildPlanTests: XCTestCase { // For completeness, the invalid target should still throw an error. XCTAssertThrows(Diagnostics.fatalError) { _ = try BuildPlan( - buildParameters: mockBuildParameters(targetTriple: .x86_64MacOS), + buildParameters: mockBuildParameters(triple: .x86_64MacOS), graph: graph, fileSystem: fileSystem, observabilityScope: observability.topScope @@ -3909,7 +3917,7 @@ final class BuildPlanTests: XCTestCase { XCTAssertThrows(Diagnostics.fatalError) { _ = try BuildPlan( - buildParameters: mockBuildParameters(targetTriple: .x86_64MacOS), + buildParameters: mockBuildParameters(triple: .x86_64MacOS), graph: graph, fileSystem: fileSystem, observabilityScope: observability.topScope @@ -4063,7 +4071,7 @@ final class BuildPlanTests: XCTestCase { func createResult(for dest: Basics.Triple) throws -> BuildPlanResult { try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(targetTriple: dest), + buildParameters: mockBuildParameters(triple: dest), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -4126,7 +4134,7 @@ final class BuildPlanTests: XCTestCase { do { let result = try BuildPlanResult(plan: BuildPlan( buildParameters: mockBuildParameters( - targetTriple: .x86_64Linux, + triple: .x86_64Linux, omitFramePointers: true ), graph: graph, @@ -4182,7 +4190,7 @@ final class BuildPlanTests: XCTestCase { do { let result = try BuildPlanResult(plan: BuildPlan( buildParameters: mockBuildParameters( - targetTriple: .x86_64Linux, + triple: .x86_64Linux, omitFramePointers: false ), graph: graph, @@ -4543,7 +4551,7 @@ final class BuildPlanTests: XCTestCase { swiftCompilerFlags: [cliFlag(tool: .swiftCompiler)], linkerFlags: [cliFlag(tool: .linker)] ), - targetTriple: targetTriple + triple: targetTriple ) let result = try BuildPlanResult(plan: BuildPlan( buildParameters: buildParameters, @@ -4828,8 +4836,9 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) + let buildParameters = mockBuildParameters() let plan = try BuildPlan( - buildParameters: mockBuildParameters(), + buildParameters: buildParameters, graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -4845,7 +4854,7 @@ final class BuildPlanTests: XCTestCase { [ .anySequence, "-emit-objc-header", - "-emit-objc-header-path", "/path/to/build/debug/Foo.build/Foo-Swift.h", + "-emit-objc-header-path", "/path/to/build/\(buildParameters.triple)/debug/Foo.build/Foo-Swift.h", .anySequence, ] ) @@ -4855,7 +4864,7 @@ final class BuildPlanTests: XCTestCase { [ .anySequence, "-emit-objc-header", - "-emit-objc-header-path", "/path/to/build/debug/Foo.build/Foo-Swift.h", + "-emit-objc-header-path", "/path/to/build/\(buildParameters.triple)/Foo.build/Foo-Swift.h", .anySequence, ] ) @@ -4865,12 +4874,12 @@ final class BuildPlanTests: XCTestCase { #if os(macOS) XCTAssertMatch( barTarget, - [.anySequence, "-fmodule-map-file=/path/to/build/debug/Foo.build/module.modulemap", .anySequence] + [.anySequence, "-fmodule-map-file=/path/to/build/\(buildParameters.triple)/debug/Foo.build/module.modulemap", .anySequence] ) #else XCTAssertNoMatch( barTarget, - [.anySequence, "-fmodule-map-file=/path/to/build/debug/Foo.build/module.modulemap", .anySequence] + [.anySequence, "-fmodule-map-file=/path/to/build/\(buildParameters.triple)/debug/Foo.build/module.modulemap", .anySequence] ) #endif @@ -4928,8 +4937,9 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) + let buildParameters = mockBuildParameters() let plan = try BuildPlan( - buildParameters: mockBuildParameters(), + buildParameters: buildParameters, graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -4946,7 +4956,7 @@ final class BuildPlanTests: XCTestCase { .anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/Foo.build/Foo-Swift.h", + "/path/to/build/\(buildParameters.triple)/debug/Foo.build/Foo-Swift.h", .anySequence, ] ) @@ -4957,7 +4967,7 @@ final class BuildPlanTests: XCTestCase { .anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/Foo.build/Foo-Swift.h", + "/path/to/build/\(buildParameters.triple)/debug/Foo.build/Foo-Swift.h", .anySequence, ] ) @@ -4969,7 +4979,7 @@ final class BuildPlanTests: XCTestCase { barTarget, [ .anySequence, - "-fmodule-map-file=/path/to/build/debug/Foo.build/module.modulemap", + "-fmodule-map-file=/path/to/build/\(buildParameters.triple)/debug/Foo.build/module.modulemap", .anySequence, ] ) @@ -4978,7 +4988,7 @@ final class BuildPlanTests: XCTestCase { barTarget, [ .anySequence, - "-fmodule-map-file=/path/to/build/debug/Foo.build/module.modulemap", + "-fmodule-map-file=/path/to/build/\(buildParameters.triple)/debug/Foo.build/module.modulemap", .anySequence, ] ) @@ -5038,8 +5048,9 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) + let buildParameters = mockBuildParameters() let plan = try BuildPlan( - buildParameters: mockBuildParameters(), + buildParameters: buildParameters, graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -5060,7 +5071,7 @@ final class BuildPlanTests: XCTestCase { .anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/Foo.build/Foo-Swift.h", + "/path/to/build/\(buildParameters.triple)/debug/Foo.build/Foo-Swift.h", .anySequence, ] ) @@ -5071,7 +5082,7 @@ final class BuildPlanTests: XCTestCase { .anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/Foo.build/Foo-Swift.h", + "/path/to/build/\(buildParameters.triple)/debug/Foo.build/Foo-Swift.h", .anySequence, ] ) @@ -5083,7 +5094,7 @@ final class BuildPlanTests: XCTestCase { barTarget, [ .anySequence, - "-fmodule-map-file=/path/to/build/debug/Foo.build/module.modulemap", + "-fmodule-map-file=/path/to/build/\(buildParameters.triple)/debug/Foo.build/module.modulemap", .anySequence, ] ) @@ -5092,7 +5103,7 @@ final class BuildPlanTests: XCTestCase { barTarget, [ .anySequence, - "-fmodule-map-file=/path/to/build/debug/Foo.build/module.modulemap", + "-fmodule-map-file=/path/to/build/\(buildParameters.triple)/debug/Foo.build/module.modulemap", .anySequence, ] ) @@ -5142,7 +5153,7 @@ final class BuildPlanTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(targetTriple: .x86_64Linux), + buildParameters: mockBuildParameters(triple: .x86_64Linux), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -5446,7 +5457,7 @@ final class BuildPlanTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) let plan = try BuildPlan( - buildParameters: mockBuildParameters(targetTriple: .wasi), + buildParameters: mockBuildParameters(triple: .wasi), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -5579,7 +5590,7 @@ final class BuildPlanTests: XCTestCase { let supportingTriples: [Basics.Triple] = [.x86_64Linux, .arm64Linux, .wasi] for triple in supportingTriples { let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true, targetTriple: triple), + buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true, triple: triple), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -5704,7 +5715,7 @@ final class BuildPlanTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(targetTriple: targetTriple), + buildParameters: mockBuildParameters(triple: targetTriple), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -5833,7 +5844,7 @@ final class BuildPlanTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(targetTriple: targetTriple), + buildParameters: mockBuildParameters(triple: targetTriple), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -6106,7 +6117,13 @@ final class BuildPlanTests: XCTestCase { observabilityScope: observability.topScope )) - switch try XCTUnwrap(result.targetMap["ExtLib"]) { + switch try XCTUnwrap( + result.targetMap[.init( + targetName: "ExtLib", + packageIdentity: "ExtPkg", + buildTriple: .destination + )] + ) { case .swift(let swiftTarget): if #available(macOS 13, *) { // `.contains` is only available in macOS 13 or newer XCTAssertTrue(try swiftTarget.compileArguments().contains(["-user-module-version", "1.0.0"])) @@ -6263,7 +6280,13 @@ final class BuildPlanTests: XCTestCase { result.checkTargetsCount(3) XCTAssertTrue(result.targetMap.values.contains { $0.target.name == "FooLogging" }) XCTAssertTrue(result.targetMap.values.contains { $0.target.name == "BarLogging" }) - let buildProduct = try XCTUnwrap(result.productMap["exe"]) + let buildProduct = try XCTUnwrap( + result.productMap[.init( + productName: "exe", + packageIdentity: "thisPkg", + buildTriple: .destination + )] + ) let dylibs = Array(buildProduct.dylibs.map({$0.product.name})).sorted() XCTAssertEqual(dylibs, ["BarLogging", "FooLogging"]) } diff --git a/Tests/BuildTests/CrossCompilationBuildTests.swift b/Tests/BuildTests/CrossCompilationBuildTests.swift new file mode 100644 index 00000000000..5026abbc443 --- /dev/null +++ b/Tests/BuildTests/CrossCompilationBuildTests.swift @@ -0,0 +1,81 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import class Basics.ObservabilitySystem +import struct Basics.Triple +import class Build.BuildPlan +import class Build.ProductBuildDescription +import class Build.SwiftTargetBuildDescription +import func SPMTestSupport.macrosPackageGraph +import func SPMTestSupport.mockBuildParameters +import struct SPMTestSupport.BuildPlanResult +import func SPMTestSupport.XCTAssertMatch +import class TSCBasic.InMemoryFileSystem + +import XCTest + +extension BuildPlanResult { + func allTargets(named name: String) throws -> [SwiftTargetBuildDescription] { + try self.targetMap + .filter { $0.0.targetName == name } + .map { try $1.swiftTarget() } + } + + func allProducts(named name: String) -> [ProductBuildDescription] { + self.productMap + .filter { $0.0.productName == name } + .map { $1 } + } + + func check(triple: Triple, for target: String, file: StaticString = #file, line: UInt = #line) throws { + let target = try self.target(for: target).swiftTarget() + XCTAssertMatch(try target.emitCommandLine(), [.contains(triple.tripleString)], file: file, line: line) + } +} + +final class CrossCompilationBuildPlanTests: XCTestCase { + func testMacros() throws { + let (graph, fs, scope) = try macrosPackageGraph() + + let productsTriple = Triple.arm64Linux + let toolsTriple = Triple.x86_64MacOS + let plan = try BuildPlan( + destinationBuildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true, triple: productsTriple), + toolsBuildParameters: mockBuildParameters(triple: toolsTriple), + graph: graph, + fileSystem: fs, + observabilityScope: scope + ) + let result = try BuildPlanResult(plan: plan) + result.checkProductsCount(3) + result.checkTargetsCount(10) + + XCTAssertTrue(try result.allTargets(named: "SwiftSyntax").contains { $0.target.buildTriple == .tools }) + try result.check(triple: toolsTriple, for: "MMIOMacros") + try result.check(triple: productsTriple, for: "MMIO") + try result.check(triple: productsTriple, for: "Core") + try result.check(triple: productsTriple, for: "HAL") + + let macroProducts = result.allProducts(named: "MMIOMacros") + XCTAssertEqual(macroProducts.count, 1) + let macroProduct = try XCTUnwrap(macroProducts.first) + XCTAssertEqual(macroProduct.buildParameters.triple, toolsTriple) + + // FIXME: check for *toolsTriple* + let mmioTarget = try XCTUnwrap(plan.targets.first { try $0.swiftTarget().target.name == "MMIO" }?.swiftTarget()) + let compileArguments = try mmioTarget.compileArguments() + XCTAssertMatch( + compileArguments, + ["-Xfrontend", "-load-plugin-executable", "-Xfrontend", .contains(toolsTriple.tripleString)] + ) + } +} diff --git a/Tests/BuildTests/LLBuildManifestBuilderTests.swift b/Tests/BuildTests/LLBuildManifestBuilderTests.swift index d577e577c7a..0450fcd8f39 100644 --- a/Tests/BuildTests/LLBuildManifestBuilderTests.swift +++ b/Tests/BuildTests/LLBuildManifestBuilderTests.swift @@ -50,7 +50,7 @@ final class LLBuildManifestBuilderTests: XCTestCase { configuration: .release )) var plan = try BuildPlan( - productsBuildParameters: buildParameters, + destinationBuildParameters: buildParameters, toolsBuildParameters: buildParameters, graph: graph, fileSystem: fs, @@ -67,8 +67,8 @@ final class LLBuildManifestBuilderTests: XCTestCase { ) try llbuild.createProductCommand(buildProduct) - let basicReleaseCommandNames = [ - AbsolutePath("/path/to/build/release/exe.product/Objects.LinkFileList").pathString, + var basicReleaseCommandNames = [ + AbsolutePath("/path/to/build/\(buildParameters.triple)/release/exe.product/Objects.LinkFileList").pathString, "", "C.exe-release.exe", ] @@ -85,7 +85,7 @@ final class LLBuildManifestBuilderTests: XCTestCase { configuration: .debug )) plan = try BuildPlan( - productsBuildParameters: buildParameters, + destinationBuildParameters: buildParameters, toolsBuildParameters: buildParameters, graph: graph, fileSystem: fs, @@ -95,12 +95,12 @@ final class LLBuildManifestBuilderTests: XCTestCase { result = try BuildPlanResult(plan: plan) buildProduct = try result.buildProduct(for: "exe") - llbuild = LLBuildManifestBuilder(plan, fileSystem: localFileSystem, observabilityScope: observability.topScope) + llbuild = LLBuildManifestBuilder(plan, fileSystem: fs, observabilityScope: observability.topScope) try llbuild.createProductCommand(buildProduct) let entitlementsCommandName = "C.exe-debug.exe-entitlements" - let basicDebugCommandNames = [ - AbsolutePath("/path/to/build/debug/exe.product/Objects.LinkFileList").pathString, + var basicDebugCommandNames = [ + AbsolutePath("/path/to/build/\(buildParameters.triple)/debug/exe.product/Objects.LinkFileList").pathString, "", "C.exe-debug.exe", ] @@ -108,7 +108,7 @@ final class LLBuildManifestBuilderTests: XCTestCase { XCTAssertEqual( llbuild.manifest.commands.map(\.key).sorted(), (basicDebugCommandNames + [ - AbsolutePath("/path/to/build/debug/exe-entitlement.plist").pathString, + AbsolutePath("/path/to/build/\(buildParameters.triple)/debug/exe-entitlement.plist").pathString, entitlementsCommandName, ]).sorted() ) @@ -121,8 +121,8 @@ final class LLBuildManifestBuilderTests: XCTestCase { XCTAssertEqual( entitlementsCommand.inputs, [ - .file("/path/to/build/debug/exe", isMutated: true), - .file("/path/to/build/debug/exe-entitlement.plist"), + .file("/path/to/build/\(buildParameters.triple)/debug/exe", isMutated: true), + .file("/path/to/build/\(buildParameters.triple)/debug/exe-entitlement.plist"), ] ) XCTAssertEqual( @@ -139,7 +139,7 @@ final class LLBuildManifestBuilderTests: XCTestCase { configuration: .release )) plan = try BuildPlan( - productsBuildParameters: buildParameters, + destinationBuildParameters: buildParameters, toolsBuildParameters: buildParameters, graph: graph, fileSystem: fs, @@ -152,6 +152,12 @@ final class LLBuildManifestBuilderTests: XCTestCase { llbuild = LLBuildManifestBuilder(plan, fileSystem: localFileSystem, observabilityScope: observability.topScope) try llbuild.createProductCommand(buildProduct) + basicReleaseCommandNames = [ + AbsolutePath("/path/to/build/\(buildParameters.triple)/release/exe.product/Objects.LinkFileList").pathString, + "", + "C.exe-release.exe", + ] + XCTAssertEqual( llbuild.manifest.commands.map(\.key).sorted(), basicReleaseCommandNames.sorted() @@ -164,7 +170,7 @@ final class LLBuildManifestBuilderTests: XCTestCase { configuration: .debug )) plan = try BuildPlan( - productsBuildParameters: buildParameters, + destinationBuildParameters: buildParameters, toolsBuildParameters: buildParameters, graph: graph, fileSystem: fs, @@ -174,12 +180,38 @@ final class LLBuildManifestBuilderTests: XCTestCase { result = try BuildPlanResult(plan: plan) buildProduct = try result.buildProduct(for: "exe") - llbuild = LLBuildManifestBuilder(plan, fileSystem: localFileSystem, observabilityScope: observability.topScope) + llbuild = LLBuildManifestBuilder(plan, fileSystem: fs, observabilityScope: observability.topScope) try llbuild.createProductCommand(buildProduct) + basicDebugCommandNames = [ + AbsolutePath("/path/to/build/\(buildParameters.triple)/debug/exe.product/Objects.LinkFileList").pathString, + "", + "C.exe-debug.exe", + ] + XCTAssertEqual( llbuild.manifest.commands.map(\.key).sorted(), basicDebugCommandNames.sorted() ) } + + /// Verifies that two targets with the same name but different triples don't share same build manifest keys. + func testToolsBuildTriple() throws { + let (graph, fs, scope) = try macrosPackageGraph() + let productsTriple = Triple.x86_64MacOS + let toolsTriple = Triple.arm64Linux + + let plan = try BuildPlan( + destinationBuildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true, triple: productsTriple), + toolsBuildParameters: mockBuildParameters(triple: toolsTriple), + graph: graph, + fileSystem: fs, + observabilityScope: scope + ) + + let builder = LLBuildManifestBuilder(plan, fileSystem: fs, observabilityScope: scope) + let manifest = try builder.generateManifest(at: "/manifest") + + XCTAssertNotNil(manifest.commands["C.SwiftSyntax-debug-tool.module"]) + } } diff --git a/Tests/BuildTests/ModuleAliasingBuildTests.swift b/Tests/BuildTests/ModuleAliasingBuildTests.swift index 090dedb431a..6101be96fb1 100644 --- a/Tests/BuildTests/ModuleAliasingBuildTests.swift +++ b/Tests/BuildTests/ModuleAliasingBuildTests.swift @@ -694,8 +694,9 @@ final class ModuleAliasingBuildTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) + let buildParameters = mockBuildParameters(shouldLinkStaticSwiftStdlib: true) let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + buildParameters: buildParameters, graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -724,33 +725,33 @@ final class ModuleAliasingBuildTests: XCTestCase { XCTAssertMatch( fooLoggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/FooLogging.build/FooLogging-Swift.h", .anySequence] + "/path/to/build/\(buildParameters.triple)/debug/FooLogging.build/FooLogging-Swift.h", .anySequence] ) XCTAssertMatch( barLoggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/BarLogging.build/BarLogging-Swift.h", .anySequence] + "/path/to/build/\(buildParameters.triple)/debug/BarLogging.build/BarLogging-Swift.h", .anySequence] ) XCTAssertMatch( loggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/Logging.build/Logging-Swift.h", .anySequence] + "/path/to/build/\(buildParameters.triple)/debug/Logging.build/Logging-Swift.h", .anySequence] ) #else XCTAssertNoMatch( fooLoggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/FooLogging.build/FooLogging-Swift.h", .anySequence] + "/path/to/build/\(buildParameters.triple)/debug/FooLogging.build/FooLogging-Swift.h", .anySequence] ) XCTAssertNoMatch( barLoggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/BarLogging.build/BarLogging-Swift.h", .anySequence] + "/path/to/build/\(buildParameters.triple)/debug/BarLogging.build/BarLogging-Swift.h", .anySequence] ) XCTAssertNoMatch( loggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/Logging.build/Logging-Swift.h", .anySequence] + "/path/to/build/\(buildParameters.triple)/debug/Logging.build/Logging-Swift.h", .anySequence] ) #endif } @@ -812,8 +813,9 @@ final class ModuleAliasingBuildTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) + let buildParameters = mockBuildParameters(shouldLinkStaticSwiftStdlib: true) let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + buildParameters: buildParameters, graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -842,23 +844,23 @@ final class ModuleAliasingBuildTests: XCTestCase { XCTAssertMatch( otherLoggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/OtherLogging.build/OtherLogging-Swift.h", .anySequence] + "/path/to/build/\(buildParameters.triple)/debug/OtherLogging.build/OtherLogging-Swift.h", .anySequence] ) XCTAssertMatch( loggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/Logging.build/Logging-Swift.h", .anySequence] + "/path/to/build/\(buildParameters.triple)/debug/Logging.build/Logging-Swift.h", .anySequence] ) #else XCTAssertNoMatch( otherLoggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/OtherLogging.build/OtherLogging-Swift.h", .anySequence] + "/path/to/build/\(buildParameters.triple)/debug/OtherLogging.build/OtherLogging-Swift.h", .anySequence] ) XCTAssertNoMatch( loggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/Logging.build/Logging-Swift.h", .anySequence] + "/path/to/build/\(buildParameters.triple)/debug/Logging.build/Logging-Swift.h", .anySequence] ) #endif } diff --git a/Tests/BuildTests/PluginsBuildPlanTests.swift b/Tests/BuildTests/PluginsBuildPlanTests.swift index 5ef99e3fc59..7b2a1a78378 100644 --- a/Tests/BuildTests/PluginsBuildPlanTests.swift +++ b/Tests/BuildTests/PluginsBuildPlanTests.swift @@ -19,7 +19,7 @@ import PackageModel final class PluginsBuildPlanTests: XCTestCase { func testBuildToolsDatabasePath() throws { try fixture(name: "Miscellaneous/Plugins/MySourceGenPlugin") { fixturePath in - let (stdout, stderr) = try executeSwiftBuild(fixturePath) + let (stdout, _) = try executeSwiftBuild(fixturePath) XCTAssertMatch(stdout, .contains("Build complete!")) XCTAssertTrue(localFileSystem.exists(fixturePath.appending(RelativePath(".build/plugins/tools/build.db")))) } @@ -48,8 +48,16 @@ final class PluginsBuildPlanTests: XCTestCase { let (stdout, stderr) = try executeSwiftPackage(fixturePath, extraArgs: ["-v", "build-plugin-dependency"]) XCTAssertMatch(stdout, .contains("Hello from dependencies-stub")) XCTAssertMatch(stderr, .contains("Build of product 'plugintool' complete!")) - XCTAssertTrue(localFileSystem.exists(fixturePath.appending(RelativePath(".build/\(hostTriple)/debug/plugintool")))) - XCTAssertTrue(localFileSystem.exists(fixturePath.appending(RelativePath(".build/\(hostTriple)/debug/placeholder")))) + XCTAssertTrue( + localFileSystem.exists( + fixturePath.appending(RelativePath(".build/\(hostTriple)/debug/plugintool-tool")) + ) + ) + XCTAssertTrue( + localFileSystem.exists( + fixturePath.appending(RelativePath(".build/\(hostTriple)/debug/placeholder")) + ) + ) } // When cross compiling the final product, plugin dependencies should still be built for the host @@ -57,8 +65,16 @@ final class PluginsBuildPlanTests: XCTestCase { let (stdout, stderr) = try executeSwiftPackage(fixturePath, extraArgs: ["--triple", targetTriple, "-v", "build-plugin-dependency"]) XCTAssertMatch(stdout, .contains("Hello from dependencies-stub")) XCTAssertMatch(stderr, .contains("Build of product 'plugintool' complete!")) - XCTAssertTrue(localFileSystem.exists(fixturePath.appending(RelativePath(".build/\(hostTriple)/debug/plugintool")))) - XCTAssertTrue(localFileSystem.exists(fixturePath.appending(RelativePath(".build/\(targetTriple)/debug/placeholder")))) + XCTAssertTrue( + localFileSystem.exists( + fixturePath.appending(RelativePath(".build/\(hostTriple)/debug/plugintool-tool")) + ) + ) + XCTAssertTrue( + localFileSystem.exists( + fixturePath.appending(RelativePath(".build/\(targetTriple)/debug/placeholder")) + ) + ) } } } diff --git a/Tests/CommandsTests/PackageToolTests.swift b/Tests/CommandsTests/PackageToolTests.swift index 4d91794c8bd..dbc6b492dad 100644 --- a/Tests/CommandsTests/PackageToolTests.swift +++ b/Tests/CommandsTests/PackageToolTests.swift @@ -1714,9 +1714,15 @@ final class PackageToolTests: CommandsTestCase { """ ) let hostTriple = try UserToolchain(swiftSDK: .hostSwiftSDK()).targetTriple - let hostTripleString = hostTriple.isDarwin() ? hostTriple.tripleString(forPlatformVersion: "") : hostTriple.tripleString - try localFileSystem.writeFileContents(packageDir.appending(components: "Binaries", "LocalBinaryTool.artifactbundle", "info.json"), string: - """ + let hostTripleString = if hostTriple.isDarwin() { + hostTriple.tripleString(forPlatformVersion: "") + } else { + hostTriple.tripleString + } + + try localFileSystem.writeFileContents( + packageDir.appending(components: "Binaries", "LocalBinaryTool.artifactbundle", "info.json"), + string: """ { "schemaVersion": "1.0", "artifacts": { "LocalBinaryTool": { @@ -1732,11 +1738,13 @@ final class PackageToolTests: CommandsTestCase { } """ ) - try localFileSystem.writeFileContents(packageDir.appending(components: "Sources", "LocalBuiltTool", "main.swift"), string: - #"print("Hello")"# + try localFileSystem.writeFileContents( + packageDir.appending(components: "Sources", "LocalBuiltTool", "main.swift"), + string: #"print("Hello")"# ) - try localFileSystem.writeFileContents(packageDir.appending(components: "Plugins", "MyPlugin", "plugin.swift"), string: - """ + try localFileSystem.writeFileContents( + packageDir.appending(components: "Plugins", "MyPlugin", "plugin.swift"), + string: """ import PackagePlugin import Foundation @main @@ -1792,8 +1800,9 @@ final class PackageToolTests: CommandsTestCase { ) // Create the sample vendored dependency package. - try localFileSystem.writeFileContents(packageDir.appending(components: "VendoredDependencies", "HelperPackage", "Package.swift"), string: - """ + try localFileSystem.writeFileContents( + packageDir.appending(components: "VendoredDependencies", "HelperPackage", "Package.swift"), + string: """ // swift-tools-version: 5.5 import PackageDescription let package = Package( @@ -1819,9 +1828,25 @@ final class PackageToolTests: CommandsTestCase { ) """ ) - try localFileSystem.writeFileContents(packageDir.appending(components: "VendoredDependencies", "HelperPackage", "Sources", "HelperLibrary", "library.swift"), string: "public func Bar() { }" + try localFileSystem.writeFileContents( + packageDir.appending( + components: "VendoredDependencies", + "HelperPackage", + "Sources", + "HelperLibrary", + "library.swift" + ), + string: "public func Bar() { }" ) - try localFileSystem.writeFileContents(packageDir.appending(components: "VendoredDependencies", "HelperPackage", "Sources", "RemoteBuiltTool", "main.swift"), string: #"print("Hello")"# + try localFileSystem.writeFileContents( + packageDir.appending( + components: "VendoredDependencies", + "HelperPackage", + "Sources", + "RemoteBuiltTool", + "main.swift" + ), + string: #"print("Hello")"# ) // Check that we can invoke the plugin with the "plugin" subcommand. diff --git a/Tests/CommandsTests/SwiftToolTests.swift b/Tests/CommandsTests/SwiftToolTests.swift index cc5c291de0e..44e8ea2c75a 100644 --- a/Tests/CommandsTests/SwiftToolTests.swift +++ b/Tests/CommandsTests/SwiftToolTests.swift @@ -255,7 +255,7 @@ final class SwiftToolTests: CommandsTestCase { let explicitDwarfOptions = try GlobalOptions.parse(["--triple", "x86_64-unknown-windows-msvc", "-debug-info-format", "dwarf"]) let explicitDwarf = try SwiftTool.createSwiftToolForTest(options: explicitDwarfOptions) plan = try BuildPlan( - productsBuildParameters: explicitDwarf.productsBuildParameters, + destinationBuildParameters: explicitDwarf.productsBuildParameters, toolsBuildParameters: explicitDwarf.toolsBuildParameters, graph: graph, fileSystem: fs, @@ -270,7 +270,7 @@ final class SwiftToolTests: CommandsTestCase { let explicitCodeView = try SwiftTool.createSwiftToolForTest(options: explicitCodeViewOptions) plan = try BuildPlan( - productsBuildParameters: explicitCodeView.productsBuildParameters, + destinationBuildParameters: explicitCodeView.productsBuildParameters, toolsBuildParameters: explicitCodeView.productsBuildParameters, graph: graph, fileSystem: fs, @@ -293,7 +293,7 @@ final class SwiftToolTests: CommandsTestCase { let implicitDwarfOptions = try GlobalOptions.parse(["--triple", "x86_64-unknown-windows-msvc"]) let implicitDwarf = try SwiftTool.createSwiftToolForTest(options: implicitDwarfOptions) plan = try BuildPlan( - productsBuildParameters: implicitDwarf.productsBuildParameters, + destinationBuildParameters: implicitDwarf.productsBuildParameters, toolsBuildParameters: implicitDwarf.toolsBuildParameters, graph: graph, fileSystem: fs, @@ -306,7 +306,7 @@ final class SwiftToolTests: CommandsTestCase { let explicitNoDebugInfoOptions = try GlobalOptions.parse(["--triple", "x86_64-unknown-windows-msvc", "-debug-info-format", "none"]) let explicitNoDebugInfo = try SwiftTool.createSwiftToolForTest(options: explicitNoDebugInfoOptions) plan = try BuildPlan( - productsBuildParameters: explicitNoDebugInfo.productsBuildParameters, + destinationBuildParameters: explicitNoDebugInfo.productsBuildParameters, toolsBuildParameters: explicitNoDebugInfo.toolsBuildParameters, graph: graph, fileSystem: fs, diff --git a/Tests/PackageGraphTests/CrossCompilationPackageGraphTests.swift b/Tests/PackageGraphTests/CrossCompilationPackageGraphTests.swift new file mode 100644 index 00000000000..79076d69adc --- /dev/null +++ b/Tests/PackageGraphTests/CrossCompilationPackageGraphTests.swift @@ -0,0 +1,61 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +@testable +import SPMTestSupport + +@testable +import PackageGraph + +import XCTest + +final class CrossCompilationPackageGraphTests: XCTestCase { + func testMacros() throws { + let graph = try macrosPackageGraph().graph + PackageGraphTester(graph) { result in + result.check(packages: "swift-firmware", "swift-mmio", "swift-syntax") + // "SwiftSyntax" is included for both host and target triples and is not pruned on this level + result.check(targets: "Core", "HAL", "MMIO", "MMIOMacros", "SwiftSyntax", "SwiftSyntax") + result.check(testModules: "CoreTests", "HALTests") + result.checkTarget("Core") { result in + result.check(buildTriple: .destination) + result.check(dependencies: "HAL") + } + result.checkTarget("HAL") { result in + result.check(buildTriple: .destination) + result.check(dependencies: "MMIO") + } + result.checkTarget("MMIO") { result in + result.check(buildTriple: .destination) + result.check(dependencies: "MMIOMacros") + } + result.checkTarget("MMIOMacros") { result in + result.check(buildTriple: .tools) + result.checkDependency("SwiftSyntax") { result in + result.checkProduct { result in + result.check(buildTriple: .tools) + result.checkTarget("SwiftSyntax") { result in + result.check(buildTriple: .tools) + } + } + } + } + + result.checkTargets("SwiftSyntax") { results in + XCTAssertEqual(results.count, 2) + + XCTAssertEqual(results.filter({ $0.target.buildTriple == .tools }).count, 1) + XCTAssertEqual(results.filter({ $0.target.buildTriple == .destination }).count, 1) + } + } + } +} diff --git a/Tests/PackageGraphTests/PackageGraphTests.swift b/Tests/PackageGraphTests/PackageGraphTests.swift index 27af5fee6c4..ab86f67d6a8 100644 --- a/Tests/PackageGraphTests/PackageGraphTests.swift +++ b/Tests/PackageGraphTests/PackageGraphTests.swift @@ -19,8 +19,7 @@ import XCTest import struct TSCBasic.ByteString import class TSCBasic.InMemoryFileSystem -class PackageGraphTests: XCTestCase { - +final class PackageGraphTests: XCTestCase { func testBasic() throws { let fs = InMemoryFileSystem(emptyFiles: "/Foo/Sources/Foo/source.swift", diff --git a/Tests/SPMBuildCoreTests/PluginInvocationTests.swift b/Tests/SPMBuildCoreTests/PluginInvocationTests.swift index d06d5bb133d..3d5da4c1255 100644 --- a/Tests/SPMBuildCoreTests/PluginInvocationTests.swift +++ b/Tests/SPMBuildCoreTests/PluginInvocationTests.swift @@ -74,7 +74,8 @@ class PluginInvocationTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) PackageGraphTester(graph) { graph in graph.check(packages: "Foo") - graph.check(targets: "Foo", "FooPlugin", "FooTool") + // "FooTool" duplicated as it's present for both build tools and end products triples. + graph.check(targets: "Foo", "FooPlugin", "FooTool", "FooTool") graph.checkTarget("Foo") { target in target.check(dependencies: "FooPlugin") } @@ -188,13 +189,13 @@ class PluginInvocationTests: XCTestCase { // Construct a canned input and run plugins using our MockPluginScriptRunner(). let outputDir = AbsolutePath("/Foo/.build") - let builtToolsDir = AbsolutePath("/path/to/build/debug") let pluginRunner = MockPluginScriptRunner() + let buildParameters = mockBuildParameters( + environment: BuildEnvironment(platform: .macOS, configuration: .debug) + ) let results = try graph.invokeBuildToolPlugins( outputDir: outputDir, - buildParameters: mockBuildParameters( - environment: BuildEnvironment(platform: .macOS, configuration: .debug) - ), + buildParameters: buildParameters, additionalFileRules: [], toolSearchDirectories: [UserToolchain.default.swiftCompilerPath.parentDirectory], pkgConfigDirectories: [], @@ -202,6 +203,7 @@ class PluginInvocationTests: XCTestCase { observabilityScope: observability.topScope, fileSystem: fileSystem ) + let builtToolsDir = AbsolutePath("/path/to/build/\(buildParameters.triple)/debug") // Check the canned output to make sure nothing was lost in transport. XCTAssertNoDiagnostics(observability.diagnostics) diff --git a/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift b/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift index 18cbcd13d14..7541f543566 100644 --- a/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift +++ b/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift @@ -42,17 +42,38 @@ class SourceKitLSPAPITests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) + let buildParameters = mockBuildParameters(shouldLinkStaticSwiftStdlib: true) let plan = try BuildPlan( - productsBuildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), - toolsBuildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + destinationBuildParameters: buildParameters, + toolsBuildParameters: buildParameters, graph: graph, fileSystem: fs, observabilityScope: observability.topScope ) let description = BuildDescription(buildPlan: plan) - try description.checkArguments(for: "exe", graph: graph, partialArguments: ["/fake/path/to/swiftc", "-module-name", "exe", "-emit-dependencies", "-emit-module", "-emit-module-path", "/path/to/build/debug/exe.build/exe.swiftmodule"]) - try description.checkArguments(for: "lib", graph: graph, partialArguments: ["/fake/path/to/swiftc", "-module-name", "lib", "-emit-dependencies", "-emit-module", "-emit-module-path", "/path/to/build/debug/Modules/lib.swiftmodule"]) + try description.checkArguments( + for: "exe", + graph: graph, + partialArguments: [ + "/fake/path/to/swiftc", + "-module-name", "exe", + "-emit-dependencies", + "-emit-module", + "-emit-module-path", "/path/to/build/\(buildParameters.triple)/debug/exe.build/exe.swiftmodule" + ] + ) + try description.checkArguments( + for: "lib", + graph: graph, + partialArguments: [ + "/fake/path/to/swiftc", + "-module-name", "lib", + "-emit-dependencies", + "-emit-module", + "-emit-module-path", "/path/to/build/\(buildParameters.triple)/debug/Modules/lib.swiftmodule" + ] + ) } } From 9c6493f76dbcb57f5482d39140aba06bfcf5a1e4 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Mon, 19 Feb 2024 15:25:52 +0000 Subject: [PATCH 011/159] Make `PackageModel` resources optional (#7348) https://github.com/apple/swift-package-manager/pull/7337 is breaking toolchain builds, so we should pass `SKIP_RESOURCE_SUPPORT` by default to avoid that. --- Package.swift | 18 +++++++++++++++--- Sources/PackageModel/UserToolchain.swift | 2 +- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/Package.swift b/Package.swift index 40d4b5f6ae9..7dcb89a018f 100644 --- a/Package.swift +++ b/Package.swift @@ -73,6 +73,19 @@ automatic linking type with `-auto` suffix appended to product's name. */ let autoProducts = [swiftPMProduct, swiftPMDataModelProduct] + +let packageModelResourcesSettings: [SwiftSetting] +let packageModelResources: [Resource] +if ProcessInfo.processInfo.environment["SWIFTPM_USE_LIBRARIES_METADATA"] == nil { + packageModelResources = [] + packageModelResourcesSettings = [.define("SKIP_RESOURCE_SUPPORT")] +} else { + packageModelResources = [ + .copy("InstalledLibrariesSupport/provided-libraries.json"), + ] + packageModelResourcesSettings = [] +} + let package = Package( name: "SwiftPM", platforms: [ @@ -218,9 +231,8 @@ let package = Package( name: "PackageModel", dependencies: ["Basics"], exclude: ["CMakeLists.txt", "README.md"], - resources: [ - .copy("InstalledLibrariesSupport/provided-libraries.json"), - ] + resources: packageModelResources, + swiftSettings: packageModelResourcesSettings ), .target( diff --git a/Sources/PackageModel/UserToolchain.swift b/Sources/PackageModel/UserToolchain.swift index 58b24986c2f..588e238ab3d 100644 --- a/Sources/PackageModel/UserToolchain.swift +++ b/Sources/PackageModel/UserToolchain.swift @@ -545,7 +545,7 @@ public final class UserToolchain: Toolchain { if let customProvidedLibraries { self.providedLibraries = customProvidedLibraries } else { - // When building with CMake, we need to skip resource support. + // When building with CMake or `swift build --build-system xcode`, we need to skip resource support. #if SKIP_RESOURCE_SUPPORT let path = self.swiftCompilerPath.parentDirectory.parentDirectory.appending(components: ["share", "pm", "provided-libraries.json"]) #else From 2c18196b9cf7ae3f7793d78b53657c6af00c8c5c Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 20 Feb 2024 01:01:18 +0000 Subject: [PATCH 012/159] Build direct dependencies of tests for target triple (#7350) `assertMacroExpansion` requires macros to be compiled for target platform and evaluated in-process. We can't handle the case of indirect macro dependencies cleanly, those will be still compiled for the host. Supersedes https://github.com/apple/swift-package-manager/pull/7349. This doesn't contain tests to expedite the fix and unblock broken CI jobs, but I've verified locally that test products of the `Examples` package in `apple/swift-syntax` repo can be built successfully and these tests all pass. --- .../SwiftTargetBuildDescription.swift | 2 +- .../Build/BuildPlan/BuildPlan+Product.swift | 4 +- .../PackageGraph/PackageGraph+Loading.swift | 5 ++- Sources/PackageGraph/PackageGraph.swift | 10 +++-- .../Resolution/ResolvedPackage.swift | 42 ++++++++++++++++++- .../Resolution/ResolvedProduct.swift | 2 +- .../Resolution/ResolvedTarget.swift | 2 +- .../CrossCompilationBuildTests.swift | 11 +++-- 8 files changed, 64 insertions(+), 14 deletions(-) diff --git a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift index 9edafd7bb9c..0bf30b71294 100644 --- a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift @@ -130,7 +130,7 @@ public final class SwiftTargetBuildDescription { self.tempsPath.appending(component: self.target.c99name + ".swiftmodule.o") } - /// The path to the swifinterface file after compilation. + /// The path to the swiftinterface file after compilation. var parseableModuleInterfaceOutputPath: AbsolutePath { self.modulesPath.appending(component: self.target.c99name + ".swiftinterface") } diff --git a/Sources/Build/BuildPlan/BuildPlan+Product.swift b/Sources/Build/BuildPlan/BuildPlan+Product.swift index 2cbe8a8c46d..a0442686232 100644 --- a/Sources/Build/BuildPlan/BuildPlan+Product.swift +++ b/Sources/Build/BuildPlan/BuildPlan+Product.swift @@ -92,13 +92,13 @@ extension BuildPlan { buildProduct.staticTargets = dependencies.staticTargets buildProduct.dylibs = try dependencies.dylibs.map { - guard let product = productMap[$0.id] else { + guard let product = self.productMap[$0.id] else { throw InternalError("unknown product \($0)") } return product } buildProduct.objects += try dependencies.staticTargets.flatMap { targetName -> [AbsolutePath] in - guard let target = targetMap[targetName.id] else { + guard let target = self.targetMap[targetName.id] else { throw InternalError("unknown target \(targetName)") } return try target.objects diff --git a/Sources/PackageGraph/PackageGraph+Loading.swift b/Sources/PackageGraph/PackageGraph+Loading.swift index 7bd38f7ab78..2c456468f1d 100644 --- a/Sources/PackageGraph/PackageGraph+Loading.swift +++ b/Sources/PackageGraph/PackageGraph+Loading.swift @@ -280,7 +280,10 @@ private func createResolvedPackages( // Resolve module aliases, if specified, for targets and their dependencies // across packages. Aliasing will result in target renaming. - let moduleAliasingUsed = try resolveModuleAliases(packageBuilders: packageBuilders, observabilityScope: observabilityScope) + let moduleAliasingUsed = try resolveModuleAliases( + packageBuilders: packageBuilders, + observabilityScope: observabilityScope + ) // Scan and validate the dependencies for packageBuilder in packageBuilders { diff --git a/Sources/PackageGraph/PackageGraph.swift b/Sources/PackageGraph/PackageGraph.swift index 6ca5fc68b59..8a5e8c7426d 100644 --- a/Sources/PackageGraph/PackageGraph.swift +++ b/Sources/PackageGraph/PackageGraph.swift @@ -79,10 +79,14 @@ public struct PackageGraph { /// Returns true if a given target is present in root packages and is not excluded for the given build environment. public func isInRootPackages(_ target: ResolvedTarget, satisfying buildEnvironment: BuildEnvironment) -> Bool { // FIXME: This can be easily cached. - return rootPackages.reduce(into: IdentifiableSet()) { (accumulator: inout IdentifiableSet, package: ResolvedPackage) in + return rootPackages.reduce( + into: IdentifiableSet() + ) { (accumulator: inout IdentifiableSet, package: ResolvedPackage) in let allDependencies = package.targets.flatMap { $0.dependencies } let unsatisfiedDependencies = allDependencies.filter { !$0.satisfies(buildEnvironment) } - let unsatisfiedDependencyTargets = unsatisfiedDependencies.compactMap { (dep: ResolvedTarget.Dependency) -> ResolvedTarget? in + let unsatisfiedDependencyTargets = unsatisfiedDependencies.compactMap { ( + dep: ResolvedTarget.Dependency + ) -> ResolvedTarget? in switch dep { case .target(let target, _): return target @@ -152,7 +156,7 @@ public struct PackageGraph { for package in self.packages { let targetsToInclude: [ResolvedTarget] if rootPackages.contains(id: package.id) { - targetsToInclude = package.targets + targetsToInclude = Array(package.targets) } else { // Don't include tests targets from non-root packages so swift-test doesn't // try to run them. diff --git a/Sources/PackageGraph/Resolution/ResolvedPackage.swift b/Sources/PackageGraph/Resolution/ResolvedPackage.swift index bf8d81eac78..e745696036a 100644 --- a/Sources/PackageGraph/Resolution/ResolvedPackage.swift +++ b/Sources/PackageGraph/Resolution/ResolvedPackage.swift @@ -11,6 +11,9 @@ //===----------------------------------------------------------------------===// import Basics + +import struct OrderedCollections.OrderedDictionary + import PackageModel /// A fully resolved package. Contains resolved targets, products and dependencies of the package. @@ -64,8 +67,43 @@ public struct ResolvedPackage { platformVersionProvider: PlatformVersionProvider ) { self.underlying = underlying - self.targets = targets - self.products = products + + var processedTargets = OrderedDictionary( + uniqueKeysWithValues: targets.map { ($0.id, $0) } + ) + var processedProducts = [ResolvedProduct]() + // Make sure that direct macro dependencies of test products are also built for the target triple. + // Without this workaround, `assertMacroExpansion` in tests can't be built, as it requires macros + // and SwiftSyntax to be built for the target triple: https://github.com/apple/swift-package-manager/pull/7349 + for var product in products { + if product.type == .test { + var targets = IdentifiableSet() + for var target in product.targets { + var dependencies = [ResolvedTarget.Dependency]() + for dependency in target.dependencies { + switch dependency { + case .target(var target, let conditions) where target.type == .macro: + target.buildTriple = .destination + dependencies.append(.target(target, conditions: conditions)) + processedTargets[target.id] = target + case .product(var product, let conditions) where product.type == .macro: + product.buildTriple = .destination + dependencies.append(.product(product, conditions: conditions)) + default: + dependencies.append(dependency) + } + } + target.dependencies = dependencies + targets.insert(target) + } + product.targets = targets + } + + processedProducts.append(product) + } + + self.products = processedProducts + self.targets = Array(processedTargets.values) self.dependencies = dependencies self.defaultLocalization = defaultLocalization self.supportedPlatforms = supportedPlatforms diff --git a/Sources/PackageGraph/Resolution/ResolvedProduct.swift b/Sources/PackageGraph/Resolution/ResolvedProduct.swift index b2d23135c2f..c63e86bfabe 100644 --- a/Sources/PackageGraph/Resolution/ResolvedProduct.swift +++ b/Sources/PackageGraph/Resolution/ResolvedProduct.swift @@ -30,7 +30,7 @@ public struct ResolvedProduct { public let underlying: Product /// The top level targets contained in this product. - public private(set) var targets: IdentifiableSet + public internal(set) var targets: IdentifiableSet /// Executable target for test entry point file. public let testEntryPointTarget: ResolvedTarget? diff --git a/Sources/PackageGraph/Resolution/ResolvedTarget.swift b/Sources/PackageGraph/Resolution/ResolvedTarget.swift index 674df077510..69864b21e22 100644 --- a/Sources/PackageGraph/Resolution/ResolvedTarget.swift +++ b/Sources/PackageGraph/Resolution/ResolvedTarget.swift @@ -133,7 +133,7 @@ public struct ResolvedTarget { public let underlying: Target /// The dependencies of this target. - public private(set) var dependencies: [Dependency] + public internal(set) var dependencies: [Dependency] /// The default localization for resources. public let defaultLocalization: String? diff --git a/Tests/BuildTests/CrossCompilationBuildTests.swift b/Tests/BuildTests/CrossCompilationBuildTests.swift index 5026abbc443..f8a9ad600c0 100644 --- a/Tests/BuildTests/CrossCompilationBuildTests.swift +++ b/Tests/BuildTests/CrossCompilationBuildTests.swift @@ -70,12 +70,17 @@ final class CrossCompilationBuildPlanTests: XCTestCase { let macroProduct = try XCTUnwrap(macroProducts.first) XCTAssertEqual(macroProduct.buildParameters.triple, toolsTriple) - // FIXME: check for *toolsTriple* + // FIXME: check for *toolsTriple* let mmioTarget = try XCTUnwrap(plan.targets.first { try $0.swiftTarget().target.name == "MMIO" }?.swiftTarget()) - let compileArguments = try mmioTarget.compileArguments() + let compileArguments = try mmioTarget.emitCommandLine() XCTAssertMatch( compileArguments, - ["-Xfrontend", "-load-plugin-executable", "-Xfrontend", .contains(toolsTriple.tripleString)] + [ + "-I", .equal(mmioTarget.moduleOutputPath.parentDirectory.pathString), + .anySequence, + "-Xfrontend", "-load-plugin-executable", + "-Xfrontend", .contains(toolsTriple.tripleString) + ] ) } } From adfbd9ad4728c288b86dc0fc0ea38404a8b88a8a Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 20 Feb 2024 19:53:46 +0000 Subject: [PATCH 013/159] Revert "Support macros when cross-compiling (#7118)" (#7352) --- CHANGELOG.md | 19 +-- .../Basics/Collections/IdentifiableSet.swift | 11 +- .../ClangTargetBuildDescription.swift | 9 +- .../ProductBuildDescription.swift | 4 +- .../ResolvedTarget+BuildDescription.swift | 23 --- .../SwiftTargetBuildDescription.swift | 147 +++++++----------- .../TargetBuildDescription.swift | 2 +- .../LLBuildManifestBuilder+Swift.swift | 36 ++--- .../LLBuildManifestBuilder.swift | 16 +- Sources/Build/BuildOperation.swift | 21 +-- .../Build/BuildPlan/BuildPlan+Product.swift | 12 +- Sources/Build/BuildPlan/BuildPlan+Swift.swift | 4 +- Sources/Build/BuildPlan/BuildPlan+Test.swift | 39 ++--- Sources/Build/BuildPlan/BuildPlan.swift | 85 +++++----- Sources/Build/CMakeLists.txt | 1 - .../Commands/PackageTools/PluginCommand.swift | 12 +- Sources/Commands/SwiftTestTool.swift | 12 +- .../Commands/Utilities/PluginDelegate.swift | 2 +- .../Utilities/SymbolGraphExtract.swift | 2 +- .../Commands/Utilities/TestingSupport.swift | 20 +-- Sources/Commands/Utilities/XCTEvents.swift | 18 +-- Sources/PackageGraph/BuildTriple.swift | 23 --- .../PackageGraph/PackageGraph+Loading.swift | 28 ++-- Sources/PackageGraph/PackageGraph.swift | 117 ++++---------- .../Resolution/ResolvedPackage.swift | 42 +---- .../Resolution/ResolvedProduct.swift | 33 +--- .../Resolution/ResolvedTarget.swift | 33 +--- Sources/PackageModel/Target/Target.swift | 2 +- .../BuildParameters/BuildParameters.swift | 11 +- .../BuildSystem/BuildSystem.swift | 2 +- .../Plugins/PluginInvocation.swift | 4 +- .../SPMTestSupport/MockBuildTestHelper.swift | 42 ++--- .../SPMTestSupport/MockPackageGraphs.swift | 122 --------------- .../SPMTestSupport/PackageGraphTester.swift | 71 +-------- Tests/BuildTests/BuildOperationTests.swift | 8 +- Tests/BuildTests/BuildPlanTests.swift | 113 ++++++-------- .../CrossCompilationBuildTests.swift | 86 ---------- .../LLBuildManifestBuilderTests.swift | 58 ++----- .../BuildTests/ModuleAliasingBuildTests.swift | 26 ++-- Tests/BuildTests/PluginsBuildPlanTests.swift | 26 +--- Tests/CommandsTests/PackageToolTests.swift | 47 ++---- Tests/CommandsTests/SwiftToolTests.swift | 8 +- .../CrossCompilationPackageGraphTests.swift | 61 -------- .../PackageGraphTests/PackageGraphTests.swift | 3 +- .../PluginInvocationTests.swift | 12 +- .../SourceKitLSPAPITests.swift | 29 +--- 46 files changed, 364 insertions(+), 1138 deletions(-) delete mode 100644 Sources/Build/BuildDescription/ResolvedTarget+BuildDescription.swift delete mode 100644 Sources/SPMTestSupport/MockPackageGraphs.swift delete mode 100644 Tests/BuildTests/CrossCompilationBuildTests.swift delete mode 100644 Tests/PackageGraphTests/CrossCompilationPackageGraphTests.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 61f95911802..fe5ed046fe5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ Note: This is in reverse chronological order, so newer entries are added to the top. -Swift 6.0 +Swift Next ----------- * [#7202] @@ -8,22 +8,11 @@ Swift 6.0 Package manifests can now access information about the Git repository the given package is in via the context object's `gitInformation` property. This allows to determine the current tag (if any), the current commit and whether or not there are uncommited changes. -* [#7201] - - `// swift-tools-version:` can now be specified on subsequent lines of `Package.swift`, for example when first few lines are required to contain a licensing comment header. - -* [#7118] - - Macros cross-compiled by SwiftPM with Swift SDKs are now correctly built, loaded, and evaluated for the host triple. - -Swift 5.10 ------------ - * [#7010] On macOS, `swift build` and `swift run` now produce binaries that allow backtraces in debug builds. Pass `SWIFT_BACKTRACE=enable=yes` environment variable to enable backtraces on such binaries when running them. -* [#7101] +* [7101] Binary artifacts are now cached along side repository checkouts so they do not need to be re-downloaded across projects. @@ -398,8 +387,4 @@ Swift 3.0 [#6276]: https://github.com/apple/swift-package-manager/pull/6276 [#6540]: https://github.com/apple/swift-package-manager/pull/6540 [#6663]: https://github.com/apple/swift-package-manager/pull/6663 -[#7010]: https://github.com/apple/swift-package-manager/pull/7010 [#7101]: https://github.com/apple/swift-package-manager/pull/7101 -[#7118]: https://github.com/apple/swift-package-manager/pull/7118 -[#7201]: https://github.com/apple/swift-package-manager/pull/7201 -[#7202]: https://github.com/apple/swift-package-manager/pull/7202 diff --git a/Sources/Basics/Collections/IdentifiableSet.swift b/Sources/Basics/Collections/IdentifiableSet.swift index 59bda6bdd85..b3bfec3071f 100644 --- a/Sources/Basics/Collections/IdentifiableSet.swift +++ b/Sources/Basics/Collections/IdentifiableSet.swift @@ -45,22 +45,13 @@ public struct IdentifiableSet: Collection { } public subscript(id: Element.ID) -> Element? { - get { - self.storage[id] - } - set { - self.storage[id] = newValue - } + self.storage[id] } public func index(after i: Index) -> Index { Index(storageIndex: self.storage.index(after: i.storageIndex)) } - public mutating func insert(_ element: Element) { - self.storage[element.id] = element - } - public func union(_ otherSequence: some Sequence) -> Self { var result = self for element in otherSequence { diff --git a/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift b/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift index cd37cd23cd1..69412b64fb3 100644 --- a/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift @@ -19,9 +19,6 @@ import struct SPMBuildCore.BuildParameters import struct SPMBuildCore.BuildToolPluginInvocationResult import struct SPMBuildCore.PrebuildCommandResult -@_spi(SwiftPMInternal) -import SPMBuildCore - import enum TSCBasic.ProcessEnv /// Target description for a Clang target i.e. C language family target. @@ -52,7 +49,7 @@ public final class ClangTargetBuildDescription { /// Path to the bundle generated for this module (if any). var bundlePath: AbsolutePath? { - guard !self.resources.isEmpty else { + guard !resources.isEmpty else { return .none } @@ -130,7 +127,7 @@ public final class ClangTargetBuildDescription { self.target = target self.toolsVersion = toolsVersion self.buildParameters = buildParameters - self.tempsPath = target.tempsPath(buildParameters) + self.tempsPath = buildParameters.buildPath.appending(component: target.c99name + ".build") self.derivedSources = Sources(paths: [], root: tempsPath.appending("DerivedSources")) // We did not use to apply package plugins to C-family targets in prior tools-versions, this preserves the behavior. @@ -222,7 +219,7 @@ public final class ClangTargetBuildDescription { if self.buildParameters.triple.isDarwin() { args += ["-fobjc-arc"] } - args += try self.buildParameters.tripleArgs(for: target) + args += try buildParameters.targetTripleArgs(for: target) args += optimizationArguments args += activeCompilationConditions diff --git a/Sources/Build/BuildDescription/ProductBuildDescription.swift b/Sources/Build/BuildDescription/ProductBuildDescription.swift index d5e0e9ba8f9..bb96cd383a2 100644 --- a/Sources/Build/BuildDescription/ProductBuildDescription.swift +++ b/Sources/Build/BuildDescription/ProductBuildDescription.swift @@ -311,7 +311,7 @@ public final class ProductBuildDescription: SPMBuildCore.ProductBuildDescription // setting is the package-level right now. We might need to figure out a better // answer for libraries if/when we support specifying deployment target at the // target-level. - args += try self.buildParameters.tripleArgs(for: self.product.targets[self.product.targets.startIndex]) + args += try self.buildParameters.targetTripleArgs(for: self.product.targets[self.product.targets.startIndex]) // Add arguments from declared build settings. args += self.buildSettingsFlags @@ -346,7 +346,7 @@ public final class ProductBuildDescription: SPMBuildCore.ProductBuildDescription // Library search path for the toolchain's copy of SwiftSyntax. #if BUILD_MACROS_AS_DYLIBS if product.type == .macro { - args += try ["-L", defaultBuildParameters.toolchain.hostLibDir.pathString] + args += try ["-L", buildParameters.toolchain.hostLibDir.pathString] } #endif diff --git a/Sources/Build/BuildDescription/ResolvedTarget+BuildDescription.swift b/Sources/Build/BuildDescription/ResolvedTarget+BuildDescription.swift deleted file mode 100644 index 10024bd0d10..00000000000 --- a/Sources/Build/BuildDescription/ResolvedTarget+BuildDescription.swift +++ /dev/null @@ -1,23 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift open source project -// -// Copyright (c) 2015-2023 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See http://swift.org/LICENSE.txt for license information -// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import struct Basics.AbsolutePath -import struct PackageGraph.ResolvedTarget - -@_spi(SwiftPMInternal) -import SPMBuildCore - -extension ResolvedTarget { - func tempsPath(_ buildParameters: BuildParameters) -> AbsolutePath { - buildParameters.buildPath.appending(component: self.c99name + "\(self.buildTriple.suffix).build") - } -} diff --git a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift index 0bf30b71294..a7216c99aa0 100644 --- a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift @@ -15,8 +15,6 @@ import Foundation import PackageGraph import PackageLoading import PackageModel - -@_spi(SwiftPMInternal) import SPMBuildCore #if USE_IMPL_ONLY_IMPORTS @@ -42,11 +40,8 @@ public final class SwiftTargetBuildDescription { /// a target is built. public let toolsVersion: ToolsVersion - /// The build parameters for this target. - let defaultBuildParameters: BuildParameters - - /// The build parameters for build tools. - let toolsBuildParameters: BuildParameters + /// The build parameters. + let buildParameters: BuildParameters /// Path to the temporary directory for this target. let tempsPath: AbsolutePath @@ -65,7 +60,7 @@ public final class SwiftTargetBuildDescription { /// Path to the bundle generated for this module (if any). var bundlePath: AbsolutePath? { if let bundleName = target.underlying.potentialBundleName, needsResourceBundle { - return self.defaultBuildParameters.bundlePath(named: bundleName) + return self.buildParameters.bundlePath(named: bundleName) } else { return .none } @@ -100,7 +95,7 @@ public final class SwiftTargetBuildDescription { let relativeSources = self.target.sources.relativePaths + self.derivedSources.relativePaths + self.pluginDerivedSources.relativePaths - let ltoEnabled = self.defaultBuildParameters.linkingParameters.linkTimeOptimizationMode != nil + let ltoEnabled = self.buildParameters.linkingParameters.linkTimeOptimizationMode != nil let objectFileExtension = ltoEnabled ? "bc" : "o" return try relativeSources.map { try AbsolutePath( @@ -111,16 +106,16 @@ public final class SwiftTargetBuildDescription { } var modulesPath: AbsolutePath { - return self.defaultBuildParameters.buildPath.appending(component: "Modules\(self.target.buildTriple.suffix)") + return self.buildParameters.buildPath.appending(component: "Modules") } /// The path to the swiftmodule file after compilation. public var moduleOutputPath: AbsolutePath { // note: needs to be public because of sourcekit-lsp // If we're an executable and we're not allowing test targets to link against us, we hide the module. - let triple = defaultBuildParameters.triple + let triple = buildParameters.triple let allowLinkingAgainstExecutables = (triple.isDarwin() || triple.isLinux() || triple.isWindows()) && self.toolsVersion >= .v5_5 let dirPath = (target.type == .executable && !allowLinkingAgainstExecutables) ? self.tempsPath : self.modulesPath - return dirPath.appending(component: "\(self.target.c99name).swiftmodule") + return dirPath.appending(component: self.target.c99name + ".swiftmodule") } /// The path to the wrapped swift module which is created using the modulewrap tool. This is required @@ -130,7 +125,7 @@ public final class SwiftTargetBuildDescription { self.tempsPath.appending(component: self.target.c99name + ".swiftmodule.o") } - /// The path to the swiftinterface file after compilation. + /// The path to the swifinterface file after compilation. var parseableModuleInterfaceOutputPath: AbsolutePath { self.modulesPath.appending(component: self.target.c99name + ".swiftinterface") } @@ -246,7 +241,7 @@ public final class SwiftTargetBuildDescription { private let shouldGenerateTestObservation: Bool /// Whether to disable sandboxing (e.g. for macros). - private let shouldDisableSandbox: Bool + private let disableSandbox: Bool /// Create a new target description with target and build parameters. init( @@ -254,14 +249,13 @@ public final class SwiftTargetBuildDescription { target: ResolvedTarget, toolsVersion: ToolsVersion, additionalFileRules: [FileRuleDescription] = [], - destinationBuildParameters: BuildParameters, - toolsBuildParameters: BuildParameters, + buildParameters: BuildParameters, buildToolPluginInvocationResults: [BuildToolPluginInvocationResult] = [], prebuildCommandResults: [PrebuildCommandResult] = [], requiredMacroProducts: [ResolvedProduct] = [], testTargetRole: TestTargetRole? = nil, shouldGenerateTestObservation: Bool = false, - shouldDisableSandbox: Bool, + disableSandbox: Bool, fileSystem: FileSystem, observabilityScope: ObservabilityScope ) throws { @@ -273,9 +267,7 @@ public final class SwiftTargetBuildDescription { self.package = package self.target = target self.toolsVersion = toolsVersion - self.defaultBuildParameters = destinationBuildParameters - self.toolsBuildParameters = toolsBuildParameters - + self.buildParameters = buildParameters // Unless mentioned explicitly, use the target type to determine if this is a test target. if let testTargetRole { self.testTargetRole = testTargetRole @@ -285,13 +277,13 @@ public final class SwiftTargetBuildDescription { self.testTargetRole = nil } - self.tempsPath = target.tempsPath(destinationBuildParameters) + self.tempsPath = buildParameters.buildPath.appending(component: target.c99name + ".build") self.derivedSources = Sources(paths: [], root: self.tempsPath.appending("DerivedSources")) self.buildToolPluginInvocationResults = buildToolPluginInvocationResults self.prebuildCommandResults = prebuildCommandResults self.requiredMacroProducts = requiredMacroProducts self.shouldGenerateTestObservation = shouldGenerateTestObservation - self.shouldDisableSandbox = shouldDisableSandbox + self.disableSandbox = disableSandbox self.fileSystem = fileSystem self.observabilityScope = observabilityScope @@ -299,7 +291,7 @@ public final class SwiftTargetBuildDescription { target: target, toolsVersion: toolsVersion, additionalFileRules: additionalFileRules, - buildParameters: destinationBuildParameters, + buildParameters: buildParameters, buildToolPluginInvocationResults: buildToolPluginInvocationResults, prebuildCommandResults: prebuildCommandResults, observabilityScope: observabilityScope @@ -336,22 +328,18 @@ public final class SwiftTargetBuildDescription { return } - guard - self.defaultBuildParameters.triple.isDarwin() && - self.defaultBuildParameters.testingParameters.experimentalTestOutput - else { + guard self.buildParameters.triple.isDarwin(), self.buildParameters.testingParameters.experimentalTestOutput else { return } - let content = generateTestObservationCode(buildParameters: self.defaultBuildParameters) + let content = generateTestObservationCode(buildParameters: self.buildParameters) // FIXME: We should generate this file during the actual build. self.derivedSources.relativePaths.append(subpath) try self.fileSystem.writeIfChanged(path: path, string: content) } - // FIXME: This will not work well for large files, as we will store the entire contents, plus its byte array - // representation in memory and also `writeIfChanged()` will read the entire generated file again. + // FIXME: This will not work well for large files, as we will store the entire contents, plus its byte array representation in memory and also `writeIfChanged()` will read the entire generated file again. private func generateResourceEmbeddingCode() throws { guard needsResourceEmbedding else { return } @@ -384,7 +372,7 @@ public final class SwiftTargetBuildDescription { guard let bundlePath else { return } let mainPathSubstitution: String - if self.defaultBuildParameters.triple.isWASI() { + if self.buildParameters.triple.isWASI() { // We prefer compile-time evaluation of the bundle path here for WASI. There's no benefit in evaluating this // at runtime, especially as `Bundle` support in WASI Foundation is partial. We expect all resource paths to // evaluate to `/\(resourceBundleName)/\(resourcePath)`, which allows us to pass this path to JS APIs like @@ -434,11 +422,7 @@ public final class SwiftTargetBuildDescription { private func packageNameArgumentIfSupported(with pkg: ResolvedPackage, packageAccess: Bool) -> [String] { let flag = "-package-name" if pkg.manifest.usePackageNameFlag, - DriverSupport.checkToolchainDriverFlags( - flags: [flag], - toolchain: self.defaultBuildParameters.toolchain, - fileSystem: self.fileSystem - ) { + DriverSupport.checkToolchainDriverFlags(flags: [flag], toolchain: self.buildParameters.toolchain, fileSystem: self.fileSystem) { if packageAccess { let pkgID = pkg.identity.description.spm_mangledToC99ExtendedIdentifier() return [flag, pkgID] @@ -452,12 +436,12 @@ public final class SwiftTargetBuildDescription { #if BUILD_MACROS_AS_DYLIBS self.requiredMacroProducts.forEach { macro in - args += ["-Xfrontend", "-load-plugin-library", "-Xfrontend", self.toolsBuildParameters.binaryPath(for: macro).pathString] + args += ["-Xfrontend", "-load-plugin-library", "-Xfrontend", self.buildParameters.binaryPath(for: macro).pathString] } #else try self.requiredMacroProducts.forEach { macro in if let macroTarget = macro.targets.first { - let executablePath = try self.toolsBuildParameters.binaryPath(for: macro).pathString + let executablePath = try self.buildParameters.binaryPath(for: macro).pathString args += ["-Xfrontend", "-load-plugin-executable", "-Xfrontend", "\(executablePath)#\(macroTarget.c99name)"] } else { throw InternalError("macro product \(macro.name) has no targets") // earlier validation should normally catch this @@ -466,14 +450,7 @@ public final class SwiftTargetBuildDescription { #endif // If we're using an OSS toolchain, add the required arguments bringing in the plugin server from the default toolchain if available. - if self.defaultBuildParameters.toolchain.isSwiftDevelopmentToolchain, - DriverSupport.checkSupportedFrontendFlags( - flags: ["-external-plugin-path"], - toolchain: self.defaultBuildParameters.toolchain, - fileSystem: self.fileSystem - ), - let pluginServer = try self.defaultBuildParameters.toolchain.swiftPluginServerPath - { + if self.buildParameters.toolchain.isSwiftDevelopmentToolchain, DriverSupport.checkSupportedFrontendFlags(flags: ["-external-plugin-path"], toolchain: self.buildParameters.toolchain, fileSystem: self.fileSystem), let pluginServer = try self.buildParameters.toolchain.swiftPluginServerPath { let toolchainUsrPath = pluginServer.parentDirectory.parentDirectory let pluginPathComponents = ["lib", "swift", "host", "plugins"] @@ -484,12 +461,8 @@ public final class SwiftTargetBuildDescription { args += ["-Xfrontend", "-external-plugin-path", "-Xfrontend", "\(localPluginPath)#\(pluginServer.pathString)"] } - if self.shouldDisableSandbox { - let toolchainSupportsDisablingSandbox = DriverSupport.checkSupportedFrontendFlags( - flags: ["-disable-sandbox"], - toolchain: self.defaultBuildParameters.toolchain, - fileSystem: fileSystem - ) + if self.disableSandbox { + let toolchainSupportsDisablingSandbox = DriverSupport.checkSupportedFrontendFlags(flags: ["-disable-sandbox"], toolchain: self.buildParameters.toolchain, fileSystem: fileSystem) if toolchainSupportsDisablingSandbox { args += ["-disable-sandbox"] } else { @@ -506,11 +479,11 @@ public final class SwiftTargetBuildDescription { /// The arguments needed to compile this target. public func compileArguments() throws -> [String] { var args = [String]() - args += try self.defaultBuildParameters.tripleArgs(for: self.target) + args += try self.buildParameters.targetTripleArgs(for: self.target) args += ["-swift-version", self.swiftVersion.rawValue] // pass `-v` during verbose builds. - if self.defaultBuildParameters.outputParameters.isVerbose { + if self.buildParameters.outputParameters.isVerbose { args += ["-v"] } @@ -518,22 +491,22 @@ public final class SwiftTargetBuildDescription { // // Technically, it should be enabled whenever WMO is off but we // don't currently make that distinction in SwiftPM - switch self.defaultBuildParameters.configuration { + switch self.buildParameters.configuration { case .debug: args += ["-enable-batch-mode"] case .release: break } - args += self.defaultBuildParameters.indexStoreArguments(for: self.target) + args += self.buildParameters.indexStoreArguments(for: self.target) args += self.optimizationArguments args += self.testingArguments - args += ["-j\(self.defaultBuildParameters.workers)"] + args += ["-j\(self.buildParameters.workers)"] args += self.activeCompilationConditions args += self.additionalFlags args += try self.moduleCacheArgs args += self.stdlibArguments - args += self.defaultBuildParameters.sanitizers.compileSwiftFlags() + args += self.buildParameters.sanitizers.compileSwiftFlags() args += ["-parseable-output"] // If we're compiling the main module of an executable other than the one that @@ -553,8 +526,8 @@ public final class SwiftTargetBuildDescription { // we can rename the symbol unconditionally. // No `-` for these flags because the set of Strings in driver.supportedFrontendFlags do // not have a leading `-` - if self.defaultBuildParameters.driverParameters.canRenameEntrypointFunctionName, - self.defaultBuildParameters.linkerFlagsForRenamingMainFunction(of: self.target) != nil + if self.buildParameters.driverParameters.canRenameEntrypointFunctionName, + self.buildParameters.linkerFlagsForRenamingMainFunction(of: self.target) != nil { args += ["-Xfrontend", "-entry-point-function-name", "-Xfrontend", "\(self.target.c99name)_main"] } @@ -567,7 +540,7 @@ public final class SwiftTargetBuildDescription { // Only add the build path to the framework search path if there are binary frameworks to link against. if !self.libraryBinaryPaths.isEmpty { - args += ["-F", self.defaultBuildParameters.buildPath.pathString] + args += ["-F", self.buildParameters.buildPath.pathString] } // Emit the ObjC compatibility header if enabled. @@ -576,12 +549,12 @@ public final class SwiftTargetBuildDescription { } // Add arguments needed for code coverage if it is enabled. - if self.defaultBuildParameters.testingParameters.enableCodeCoverage { + if self.buildParameters.testingParameters.enableCodeCoverage { args += ["-profile-coverage-mapping", "-profile-generate"] } // Add arguments to colorize output if stdout is tty - if self.defaultBuildParameters.outputParameters.isColorized { + if self.buildParameters.outputParameters.isColorized { args += ["-color-diagnostics"] } @@ -591,7 +564,7 @@ public final class SwiftTargetBuildDescription { switch testTargetRole { case .discovery: for dependency in try self.target.recursiveTargetDependencies() { - let dependencyScope = self.defaultBuildParameters.createScope(for: dependency) + let dependencyScope = self.buildParameters.createScope(for: dependency) let dependencySwiftFlags = dependencyScope.evaluate(.OTHER_SWIFT_FLAGS) if let interopModeFlag = dependencySwiftFlags.first(where: { $0.hasPrefix("-cxx-interoperability-mode=") }) { args += [interopModeFlag] @@ -611,17 +584,17 @@ public final class SwiftTargetBuildDescription { // Add the output for the `.swiftinterface`, if requested or if library evolution has been enabled some other // way. - if self.defaultBuildParameters.driverParameters.enableParseableModuleInterfaces || args.contains("-enable-library-evolution") { + if self.buildParameters.driverParameters.enableParseableModuleInterfaces || args.contains("-enable-library-evolution") { args += ["-emit-module-interface-path", self.parseableModuleInterfaceOutputPath.pathString] } - args += self.defaultBuildParameters.toolchain.extraFlags.swiftCompilerFlags + args += self.buildParameters.toolchain.extraFlags.swiftCompilerFlags // User arguments (from -Xswiftc) should follow generated arguments to allow user overrides - args += self.defaultBuildParameters.flags.swiftCompilerFlags + args += self.buildParameters.flags.swiftCompilerFlags - args += self.defaultBuildParameters.toolchain.extraFlags.cCompilerFlags.asSwiftcCCompilerFlags() + args += self.buildParameters.toolchain.extraFlags.cCompilerFlags.asSwiftcCCompilerFlags() // User arguments (from -Xcc) should follow generated arguments to allow user overrides - args += self.defaultBuildParameters.flags.cCompilerFlags.asSwiftcCCompilerFlags() + args += self.buildParameters.flags.cCompilerFlags.asSwiftcCCompilerFlags() // TODO: Pass -Xcxx flags to swiftc (#6491) // Uncomment when downstream support arrives. @@ -630,7 +603,7 @@ public final class SwiftTargetBuildDescription { // args += self.buildParameters.flags.cxxCompilerFlags.asSwiftcCXXCompilerFlags() // Enable the correct LTO mode if requested. - switch self.defaultBuildParameters.linkingParameters.linkTimeOptimizationMode { + switch self.buildParameters.linkingParameters.linkTimeOptimizationMode { case nil: break case .full: @@ -640,7 +613,7 @@ public final class SwiftTargetBuildDescription { } // Pass default include paths from the toolchain. - for includeSearchPath in self.defaultBuildParameters.toolchain.includeSearchPaths { + for includeSearchPath in self.buildParameters.toolchain.includeSearchPaths { args += ["-I", includeSearchPath.pathString] } @@ -664,7 +637,7 @@ public final class SwiftTargetBuildDescription { // rdar://117578677 // Pass -fno-omit-frame-pointer to support backtraces // this can be removed once the backtracer uses DWARF instead of frame pointers - if let omitFramePointers = self.defaultBuildParameters.debuggingParameters.omitFramePointers { + if let omitFramePointers = self.buildParameters.debuggingParameters.omitFramePointers { if omitFramePointers { args += ["-Xcc", "-fomit-frame-pointer"] } else { @@ -679,7 +652,7 @@ public final class SwiftTargetBuildDescription { /// such as emitting a module or supplementary outputs. public func emitCommandLine(scanInvocation: Bool = false) throws -> [String] { var result: [String] = [] - result.append(self.defaultBuildParameters.toolchain.swiftCompilerPath.pathString) + result.append(self.buildParameters.toolchain.swiftCompilerPath.pathString) result.append("-module-name") result.append(self.target.c99name) @@ -697,7 +670,7 @@ public final class SwiftTargetBuildDescription { result.append(try self.writeOutputFileMap().pathString) } - if self.defaultBuildParameters.useWholeModuleOptimization { + if self.buildParameters.useWholeModuleOptimization { result.append("-whole-module-optimization") result.append("-num-threads") result.append(String(ProcessInfo.processInfo.activeProcessorCount)) @@ -717,7 +690,7 @@ public final class SwiftTargetBuildDescription { /// Returns true if ObjC compatibility header should be emitted. private var shouldEmitObjCCompatibilityHeader: Bool { - self.defaultBuildParameters.triple.isDarwin() && self.target.type == .library + self.buildParameters.triple.isDarwin() && self.target.type == .library } func writeOutputFileMap() throws -> AbsolutePath { @@ -731,7 +704,7 @@ public final class SwiftTargetBuildDescription { """# - if self.defaultBuildParameters.useWholeModuleOptimization { + if self.buildParameters.useWholeModuleOptimization { let moduleName = self.target.c99name content += #""" @@ -762,7 +735,7 @@ public final class SwiftTargetBuildDescription { // Write out the entries for each source file. let sources = self.sources let objects = try self.objects - let ltoEnabled = self.defaultBuildParameters.linkingParameters.linkTimeOptimizationMode != nil + let ltoEnabled = self.buildParameters.linkingParameters.linkTimeOptimizationMode != nil let objectKey = ltoEnabled ? "llvm-bc" : "object" for idx in 0.. [String] { - let scope = self.defaultBuildParameters.createScope(for: self.target) + let scope = self.buildParameters.createScope(for: self.target) var flags: [String] = [] // Swift defines. @@ -865,7 +838,7 @@ public final class SwiftTargetBuildDescription { // Include path for the toolchain's copy of SwiftSyntax. #if BUILD_MACROS_AS_DYLIBS if target.type == .macro { - flags += try ["-I", self.defaultBuildParameters.toolchain.hostLibDir.pathString] + flags += try ["-I", self.buildParameters.toolchain.hostLibDir.pathString] } #endif @@ -876,7 +849,7 @@ public final class SwiftTargetBuildDescription { private var activeCompilationConditions: [String] { var compilationConditions = ["-DSWIFT_PACKAGE"] - switch self.defaultBuildParameters.configuration { + switch self.buildParameters.configuration { case .debug: compilationConditions += ["-DDEBUG"] case .release: @@ -888,7 +861,7 @@ public final class SwiftTargetBuildDescription { /// Optimization arguments according to the build configuration. private var optimizationArguments: [String] { - switch self.defaultBuildParameters.configuration { + switch self.buildParameters.configuration { case .debug: return ["-Onone"] case .release: @@ -902,7 +875,7 @@ public final class SwiftTargetBuildDescription { // test targets must be built with -enable-testing // since its required for test discovery (the non objective-c reflection kind) return ["-enable-testing"] - } else if self.defaultBuildParameters.testingParameters.enableTestability { + } else if self.buildParameters.testingParameters.enableTestability { return ["-enable-testing"] } else { return [] @@ -912,20 +885,20 @@ public final class SwiftTargetBuildDescription { /// Module cache arguments. private var moduleCacheArgs: [String] { get throws { - ["-module-cache-path", try self.defaultBuildParameters.moduleCache.pathString] + ["-module-cache-path", try self.buildParameters.moduleCache.pathString] } } private var stdlibArguments: [String] { var arguments: [String] = [] - let isLinkingStaticStdlib = self.defaultBuildParameters.linkingParameters.shouldLinkStaticSwiftStdlib - && self.defaultBuildParameters.triple.isSupportingStaticStdlib + let isLinkingStaticStdlib = self.buildParameters.linkingParameters.shouldLinkStaticSwiftStdlib + && self.buildParameters.triple.isSupportingStaticStdlib if isLinkingStaticStdlib { arguments += ["-static-stdlib"] } - if let resourcesPath = self.defaultBuildParameters.toolchain.swiftResourcesPath(isStatic: isLinkingStaticStdlib) { + if let resourcesPath = self.buildParameters.toolchain.swiftResourcesPath(isStatic: isLinkingStaticStdlib) { arguments += ["-resource-dir", "\(resourcesPath)"] } diff --git a/Sources/Build/BuildDescription/TargetBuildDescription.swift b/Sources/Build/BuildDescription/TargetBuildDescription.swift index ad9de913c2d..4fae9198680 100644 --- a/Sources/Build/BuildDescription/TargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/TargetBuildDescription.swift @@ -101,7 +101,7 @@ public enum TargetBuildDescription { var buildParameters: BuildParameters { switch self { case .swift(let swiftTargetBuildDescription): - return swiftTargetBuildDescription.defaultBuildParameters + return swiftTargetBuildDescription.buildParameters case .clang(let clangTargetBuildDescription): return clangTargetBuildDescription.buildParameters } diff --git a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift index 116467633e5..a51bf24d954 100644 --- a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift +++ b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift @@ -45,7 +45,7 @@ extension LLBuildManifestBuilder { let moduleNode = Node.file(target.moduleOutputPath) let cmdOutputs = objectNodes + [moduleNode] - if target.defaultBuildParameters.driverParameters.useIntegratedSwiftDriver { + if target.buildParameters.driverParameters.useIntegratedSwiftDriver { try self.addSwiftCmdsViaIntegratedDriver( target, inputs: inputs, @@ -68,7 +68,7 @@ extension LLBuildManifestBuilder { // jobs needed to build this Swift target. var commandLine = try target.emitCommandLine() commandLine.append("-driver-use-frontend-path") - commandLine.append(target.defaultBuildParameters.toolchain.swiftCompilerPath.pathString) + commandLine.append(target.buildParameters.toolchain.swiftCompilerPath.pathString) // FIXME: At some point SwiftPM should provide its own executor for // running jobs/launching processes during planning let resolver = try ArgsResolver(fileSystem: target.fileSystem) @@ -132,7 +132,7 @@ extension LLBuildManifestBuilder { // common intermediate dependency modules, such dependencies can lead // to cycles in the resulting manifest. var manifestNodeInputs: [Node] = [] - if targetDescription.defaultBuildParameters.driverParameters.useExplicitModuleBuild && !isMainModule(job) { + if targetDescription.buildParameters.driverParameters.useExplicitModuleBuild && !isMainModule(job) { manifestNodeInputs = jobInputs } else { manifestNodeInputs = (inputs + jobInputs).uniqued() @@ -192,9 +192,7 @@ extension LLBuildManifestBuilder { // Sort the product targets in topological order in order to collect and "bubble up" // their respective dependency graphs to the depending targets. let nodes: [ResolvedTarget.Dependency] = try self.plan.targetMap.keys.compactMap { - guard let target = self.plan.graph.allTargets[$0] else { - throw InternalError("unknown target \($0)") - } + guard let target = self.plan.graph.allTargets[$0] else { throw InternalError("unknown target \($0)") } return ResolvedTarget.Dependency.target(target, conditions: []) } let allPackageDependencies = try topologicalSort(nodes, successors: { $0.dependencies }) @@ -287,7 +285,7 @@ extension LLBuildManifestBuilder { // jobs needed to build this Swift target. var commandLine = try targetDescription.emitCommandLine() commandLine.append("-driver-use-frontend-path") - commandLine.append(targetDescription.defaultBuildParameters.toolchain.swiftCompilerPath.pathString) + commandLine.append(targetDescription.buildParameters.toolchain.swiftCompilerPath.pathString) commandLine.append("-experimental-explicit-module-build") let resolver = try ArgsResolver(fileSystem: self.fileSystem) let executor = SPMSwiftDriverExecutor( @@ -378,14 +376,14 @@ extension LLBuildManifestBuilder { cmdOutputs: [Node] ) throws { let isLibrary = target.target.type == .library || target.target.type == .test - let cmdName = target.target.getCommandName(config: target.defaultBuildParameters.buildConfig) + let cmdName = target.target.getCommandName(config: target.buildParameters.buildConfig) self.manifest.addWriteSourcesFileListCommand(sources: target.sources, sourcesFileListPath: target.sourcesFileListPath) self.manifest.addSwiftCmd( name: cmdName, inputs: inputs + [Node.file(target.sourcesFileListPath)], outputs: cmdOutputs, - executable: target.defaultBuildParameters.toolchain.swiftCompilerPath, + executable: target.buildParameters.toolchain.swiftCompilerPath, moduleName: target.target.c99name, moduleAliases: target.target.moduleAliases, moduleOutputPath: target.moduleOutputPath, @@ -396,7 +394,7 @@ extension LLBuildManifestBuilder { sources: target.sources, fileList: target.sourcesFileListPath, isLibrary: isLibrary, - wholeModuleOptimization: target.defaultBuildParameters.configuration == .release, + wholeModuleOptimization: target.buildParameters.configuration == .release, outputFileMapPath: try target.writeOutputFileMap() // FIXME: Eliminate side effect. ) } @@ -406,7 +404,7 @@ extension LLBuildManifestBuilder { ) throws -> [Node] { var inputs = target.sources.map(Node.file) - let swiftVersionFilePath = addSwiftGetVersionCommand(buildParameters: target.defaultBuildParameters) + let swiftVersionFilePath = addSwiftGetVersionCommand(buildParameters: target.buildParameters) inputs.append(.file(swiftVersionFilePath)) // Add resources node as the input to the target. This isn't great because we @@ -452,7 +450,7 @@ extension LLBuildManifestBuilder { } } - for dependency in target.target.dependencies(satisfying: target.defaultBuildParameters.buildEnvironment) { + for dependency in target.target.dependencies(satisfying: target.buildParameters.buildEnvironment) { switch dependency { case .target(let target, _): try addStaticTargetInputs(target) @@ -479,7 +477,7 @@ extension LLBuildManifestBuilder { } for binaryPath in target.libraryBinaryPaths { - let path = target.defaultBuildParameters.destinationPath(forBinaryAt: binaryPath) + let path = target.buildParameters.destinationPath(forBinaryAt: binaryPath) if self.fileSystem.isDirectory(binaryPath) { inputs.append(directory: path) } else { @@ -491,7 +489,7 @@ extension LLBuildManifestBuilder { // Depend on any required macro product's output. try target.requiredMacroProducts.forEach { macro in - try inputs.append(.virtual(macro.getLLBuildTargetName(config: target.defaultBuildParameters.buildConfig))) + try inputs.append(.virtual(macro.getLLBuildTargetName(config: target.buildParameters.buildConfig))) } return inputs + additionalInputs @@ -500,7 +498,7 @@ extension LLBuildManifestBuilder { /// Adds a top-level phony command that builds the entire target. private func addTargetCmd(_ target: SwiftTargetBuildDescription, cmdOutputs: [Node]) { // Create a phony node to represent the entire target. - let targetName = target.target.getLLBuildTargetName(config: target.defaultBuildParameters.buildConfig) + let targetName = target.target.getLLBuildTargetName(config: target.buildParameters.buildConfig) let targetOutput: Node = .virtual(targetName) self.manifest.addNode(targetOutput, toTarget: targetName) @@ -509,7 +507,7 @@ extension LLBuildManifestBuilder { inputs: cmdOutputs, outputs: [targetOutput] ) - if self.plan.graph.isInRootPackages(target.target, satisfying: target.defaultBuildParameters.buildEnvironment) { + if self.plan.graph.isInRootPackages(target.target, satisfying: target.buildParameters.buildEnvironment) { if !target.isTestTarget { self.addNode(targetOutput, toTarget: .main) } @@ -519,13 +517,13 @@ extension LLBuildManifestBuilder { private func addModuleWrapCmd(_ target: SwiftTargetBuildDescription) throws { // Add commands to perform the module wrapping Swift modules when debugging strategy is `modulewrap`. - guard target.defaultBuildParameters.debuggingStrategy == .modulewrap else { return } + guard target.buildParameters.debuggingStrategy == .modulewrap else { return } var moduleWrapArgs = [ - target.defaultBuildParameters.toolchain.swiftCompilerPath.pathString, + target.buildParameters.toolchain.swiftCompilerPath.pathString, "-modulewrap", target.moduleOutputPath.pathString, "-o", target.wrappedModuleOutputPath.pathString, ] - moduleWrapArgs += try target.defaultBuildParameters.tripleArgs(for: target.target) + moduleWrapArgs += try target.buildParameters.targetTripleArgs(for: target.target) self.manifest.addShellCmd( name: target.wrappedModuleOutputPath.pathString, description: "Wrapping AST for \(target.target.name) for debugging", diff --git a/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift b/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift index 63038ce6a8a..4d9b6d62787 100644 --- a/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift +++ b/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift @@ -14,8 +14,6 @@ import Basics import LLBuildManifest import PackageGraph import PackageModel - -@_spi(SwiftPMInternal) import SPMBuildCore #if USE_IMPL_ONLY_IMPORTS @@ -324,26 +322,26 @@ extension ResolvedTarget { } public func getLLBuildTargetName(config: String) -> String { - "\(self.name)-\(config)\(self.buildTriple.suffix).module" + "\(name)-\(config).module" } public func getLLBuildResourcesCmdName(config: String) -> String { - "\(self.name)-\(config).module-resources" + "\(name)-\(config).module-resources" } } extension ResolvedProduct { public func getLLBuildTargetName(config: String) throws -> String { - let potentialExecutableTargetName = "\(name)-\(config)\(self.buildTriple.suffix).exe" - let potentialLibraryTargetName = "\(name)-\(config)\(self.buildTriple.suffix).dylib" + let potentialExecutableTargetName = "\(name)-\(config).exe" + let potentialLibraryTargetName = "\(name)-\(config).dylib" switch type { case .library(.dynamic): return potentialLibraryTargetName case .test: - return "\(name)-\(config)\(self.buildTriple.suffix).test" + return "\(name)-\(config).test" case .library(.static): - return "\(name)-\(config)\(self.buildTriple.suffix).a" + return "\(name)-\(config).a" case .library(.automatic): throw InternalError("automatic library not supported") case .executable, .snippet: @@ -360,7 +358,7 @@ extension ResolvedProduct { } public func getCommandName(config: String) throws -> String { - try "C.\(self.getLLBuildTargetName(config: config))\(self.buildTriple.suffix)" + try "C." + self.getLLBuildTargetName(config: config) } } diff --git a/Sources/Build/BuildOperation.swift b/Sources/Build/BuildOperation.swift index d661ea9fdd5..fab2429cccf 100644 --- a/Sources/Build/BuildOperation.swift +++ b/Sources/Build/BuildOperation.swift @@ -13,10 +13,7 @@ @_spi(SwiftPMInternal) import Basics import LLBuildManifest - -@_spi(SwiftPMInternal) import PackageGraph - import PackageLoading import PackageModel import SPMBuildCore @@ -272,7 +269,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS } // TODO: Currently this function will only match frameworks. - func detectUnexpressedDependencies( + internal func detectUnexpressedDependencies( availableLibraries: [LibraryMetadata], targetDependencyMap: [String: [String]]? ) { @@ -298,9 +295,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS } let usedSDKDependencies: [String] = Set(possibleTempsPaths).flatMap { possibleTempsPath in - guard let contents = try? self.fileSystem.readFileContents( - possibleTempsPath.appending(component: "\(c99name).d") - ) else { + guard let contents = try? self.fileSystem.readFileContents(possibleTempsPath.appending(component: "\(c99name).d")) else { return [String]() } @@ -543,20 +538,17 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS // Invoke any build tool plugins in the graph to generate prebuild commands and build commands. if let pluginConfiguration, !self.productsBuildParameters.shouldSkipBuilding { // Hacky workaround for rdar://120560817, but it replicates precisely enough the original behavior before - // products/tools build parameters were split. Ideally we want to specify the correct path at the time + // products/tools build parameters were split. Ideally we want to have specify the correct path at the time // when `toolsBuildParameters` is initialized, but we have too many places in the codebase where that's // done, which makes it hard to realign them all at once. var pluginsBuildParameters = self.toolsBuildParameters pluginsBuildParameters.dataPath = pluginsBuildParameters.dataPath.parentDirectory.appending(components: ["plugins", "tools"]) - var buildToolsGraph = graph - try buildToolsGraph.updateBuildTripleRecursively(.tools) - let buildOperationForPluginDependencies = BuildOperation( // FIXME: this doesn't maintain the products/tools split cleanly productsBuildParameters: pluginsBuildParameters, toolsBuildParameters: pluginsBuildParameters, cacheBuildManifest: false, - packageGraphLoader: { buildToolsGraph }, + packageGraphLoader: { return graph }, additionalFileRules: self.additionalFileRules, pkgConfigDirectories: self.pkgConfigDirectories, dependenciesByRootPackageIdentity: [:], @@ -566,7 +558,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS fileSystem: self.fileSystem, observabilityScope: self.observabilityScope ) - buildToolPluginInvocationResults = try buildToolsGraph.invokeBuildToolPlugins( + buildToolPluginInvocationResults = try graph.invokeBuildToolPlugins( outputDir: pluginConfiguration.workDirectory.appending("outputs"), buildParameters: pluginsBuildParameters, additionalFileRules: self.additionalFileRules, @@ -584,6 +576,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS } } + // Surface any diagnostics from build tool plugins. var succeeded = true for (_, (target, results)) in buildToolPluginInvocationResults { @@ -663,7 +656,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS // Create the build plan based, on the graph and any information from plugins. let plan = try BuildPlan( - destinationBuildParameters: self.productsBuildParameters, + productsBuildParameters: self.productsBuildParameters, toolsBuildParameters: self.toolsBuildParameters, graph: graph, additionalFileRules: additionalFileRules, diff --git a/Sources/Build/BuildPlan/BuildPlan+Product.swift b/Sources/Build/BuildPlan/BuildPlan+Product.swift index a0442686232..d14ec2418ad 100644 --- a/Sources/Build/BuildPlan/BuildPlan+Product.swift +++ b/Sources/Build/BuildPlan/BuildPlan+Product.swift @@ -70,7 +70,7 @@ extension BuildPlan { switch target.underlying { case is SwiftTarget: // Swift targets are guaranteed to have a corresponding Swift description. - guard case .swift(let description) = self.targetMap[target.id] else { + guard case .swift(let description) = targetMap[target.id] else { throw InternalError("unknown target \(target)") } @@ -92,13 +92,13 @@ extension BuildPlan { buildProduct.staticTargets = dependencies.staticTargets buildProduct.dylibs = try dependencies.dylibs.map { - guard let product = self.productMap[$0.id] else { + guard let product = productMap[$0.id] else { throw InternalError("unknown product \($0)") } return product } buildProduct.objects += try dependencies.staticTargets.flatMap { targetName -> [AbsolutePath] in - guard let target = self.targetMap[targetName.id] else { + guard let target = targetMap[targetName.id] else { throw InternalError("unknown target \(targetName)") } return try target.objects @@ -220,11 +220,9 @@ extension BuildPlan { if product.targets.contains(id: target.id) { staticTargets.append(target) } - // Library targets should always be included for the same build triple. + // Library targets should always be included. case .library: - if target.buildTriple == product.buildTriple { - staticTargets.append(target) - } + staticTargets.append(target) // Add system target to system targets array. case .systemModule: systemModules.append(target) diff --git a/Sources/Build/BuildPlan/BuildPlan+Swift.swift b/Sources/Build/BuildPlan/BuildPlan+Swift.swift index 058631598f2..36b1cacde0c 100644 --- a/Sources/Build/BuildPlan/BuildPlan+Swift.swift +++ b/Sources/Build/BuildPlan/BuildPlan+Swift.swift @@ -19,7 +19,7 @@ extension BuildPlan { func plan(swiftTarget: SwiftTargetBuildDescription) throws { // We need to iterate recursive dependencies because Swift compiler needs to see all the targets a target // depends on. - let environment = swiftTarget.defaultBuildParameters.buildEnvironment + let environment = swiftTarget.buildParameters.buildEnvironment for case .target(let dependency, _) in try swiftTarget.target.recursiveDependencies(satisfying: environment) { switch dependency.underlying { case let underlyingTarget as ClangTarget where underlyingTarget.type == .library: @@ -40,7 +40,7 @@ extension BuildPlan { swiftTarget.additionalFlags += try pkgConfig(for: target).cFlags case let target as BinaryTarget: if case .xcframework = target.kind { - let libraries = try self.parseXCFramework(for: target, triple: swiftTarget.defaultBuildParameters.triple) + let libraries = try self.parseXCFramework(for: target, triple: swiftTarget.buildParameters.triple) for library in libraries { library.headersPaths.forEach { swiftTarget.additionalFlags += ["-I", $0.pathString, "-Xcc", "-I", "-Xcc", $0.pathString] diff --git a/Sources/Build/BuildPlan/BuildPlan+Test.swift b/Sources/Build/BuildPlan/BuildPlan+Test.swift index 9d11d012d6e..93fd9da29c7 100644 --- a/Sources/Build/BuildPlan/BuildPlan+Test.swift +++ b/Sources/Build/BuildPlan/BuildPlan+Test.swift @@ -26,16 +26,15 @@ import protocol TSCBasic.FileSystem extension BuildPlan { static func makeDerivedTestTargets( - destinationBuildParameters: BuildParameters, - toolsBuildParameters: BuildParameters, + _ buildParameters: BuildParameters, _ graph: PackageGraph, - shouldDisableSandbox: Bool, + _ disableSandbox: Bool, _ fileSystem: FileSystem, _ observabilityScope: ObservabilityScope ) throws -> [(product: ResolvedProduct, discoveryTargetBuildDescription: SwiftTargetBuildDescription?, entryPointTargetBuildDescription: SwiftTargetBuildDescription)] { - guard destinationBuildParameters.testingParameters.testProductStyle.requiresAdditionalDerivedTestTargets, - case .entryPointExecutable(let explicitlyEnabledDiscovery, let explicitlySpecifiedPath) = - destinationBuildParameters.testingParameters.testProductStyle + guard buildParameters.testingParameters.testProductStyle.requiresAdditionalDerivedTestTargets, + case .entryPointExecutable(let explicitlyEnabledDiscovery, let explicitlySpecifiedPath) = + buildParameters.testingParameters.testProductStyle else { throw InternalError("makeTestManifestTargets should not be used for build plan which does not require additional derived test targets") } @@ -69,7 +68,7 @@ extension BuildPlan { /// Generates test discovery targets, which contain derived sources listing the discovered tests. func generateDiscoveryTargets() throws -> (target: SwiftTarget, resolved: ResolvedTarget, buildDescription: SwiftTargetBuildDescription) { let discoveryTargetName = "\(package.manifest.displayName)PackageDiscoveredTests" - let discoveryDerivedDir = destinationBuildParameters.buildPath.appending(components: "\(discoveryTargetName).derived") + let discoveryDerivedDir = buildParameters.buildPath.appending(components: "\(discoveryTargetName).derived") let discoveryMainFile = discoveryDerivedDir.appending(component: TestDiscoveryTool.mainFileName) var discoveryPaths: [AbsolutePath] = [] @@ -97,10 +96,9 @@ extension BuildPlan { package: package, target: discoveryResolvedTarget, toolsVersion: toolsVersion, - destinationBuildParameters: destinationBuildParameters, - toolsBuildParameters: toolsBuildParameters, + buildParameters: buildParameters, testTargetRole: .discovery, - shouldDisableSandbox: shouldDisableSandbox, + disableSandbox: disableSandbox, fileSystem: fileSystem, observabilityScope: observabilityScope ) @@ -114,8 +112,8 @@ extension BuildPlan { swiftTargetDependencies: [Target.Dependency], resolvedTargetDependencies: [ResolvedTarget.Dependency] ) throws -> SwiftTargetBuildDescription { - let entryPointDerivedDir = destinationBuildParameters.buildPath.appending(components: "\(testProduct.name).derived") - let entryPointMainFileName = TestEntryPointTool.mainFileName(for: destinationBuildParameters.testingParameters.library) + let entryPointDerivedDir = buildParameters.buildPath.appending(components: "\(testProduct.name).derived") + let entryPointMainFileName = TestEntryPointTool.mainFileName(for: buildParameters.testingParameters.library) let entryPointMainFile = entryPointDerivedDir.appending(component: entryPointMainFileName) let entryPointSources = Sources(paths: [entryPointMainFile], root: entryPointDerivedDir) @@ -138,10 +136,9 @@ extension BuildPlan { package: package, target: entryPointResolvedTarget, toolsVersion: toolsVersion, - destinationBuildParameters: destinationBuildParameters, - toolsBuildParameters: toolsBuildParameters, + buildParameters: buildParameters, testTargetRole: .entryPoint(isSynthesized: true), - shouldDisableSandbox: shouldDisableSandbox, + disableSandbox: disableSandbox, fileSystem: fileSystem, observabilityScope: observabilityScope ) @@ -151,7 +148,7 @@ extension BuildPlan { let swiftTargetDependencies: [Target.Dependency] let resolvedTargetDependencies: [ResolvedTarget.Dependency] - switch destinationBuildParameters.testingParameters.library { + switch buildParameters.testingParameters.library { case .xctest: discoveryTargets = try generateDiscoveryTargets() swiftTargetDependencies = [.target(discoveryTargets!.target, conditions: [])] @@ -184,10 +181,9 @@ extension BuildPlan { package: package, target: entryPointResolvedTarget, toolsVersion: toolsVersion, - destinationBuildParameters: destinationBuildParameters, - toolsBuildParameters: toolsBuildParameters, + buildParameters: buildParameters, testTargetRole: .entryPoint(isSynthesized: false), - shouldDisableSandbox: shouldDisableSandbox, + disableSandbox: disableSandbox, fileSystem: fileSystem, observabilityScope: observabilityScope ) @@ -207,10 +203,9 @@ extension BuildPlan { package: package, target: entryPointResolvedTarget, toolsVersion: toolsVersion, - destinationBuildParameters: destinationBuildParameters, - toolsBuildParameters: toolsBuildParameters, + buildParameters: buildParameters, testTargetRole: .entryPoint(isSynthesized: false), - shouldDisableSandbox: shouldDisableSandbox, + disableSandbox: disableSandbox, fileSystem: fileSystem, observabilityScope: observabilityScope ) diff --git a/Sources/Build/BuildPlan/BuildPlan.swift b/Sources/Build/BuildPlan/BuildPlan.swift index 2c4c7519fd8..1f4eda79e5f 100644 --- a/Sources/Build/BuildPlan/BuildPlan.swift +++ b/Sources/Build/BuildPlan/BuildPlan.swift @@ -127,13 +127,13 @@ extension BuildParameters { return [] } - public func tripleArgs(for target: ResolvedTarget) throws -> [String] { - // confusingly enough this is the triple argument, not the target argument + /// Computes the target triple arguments for a given resolved target. + public func targetTripleArgs(for target: ResolvedTarget) throws -> [String] { var args = ["-target"] // Compute the triple string for Darwin platform using the platform version. if self.triple.isDarwin() { - let platform = self.buildEnvironment.platform + let platform = buildEnvironment.platform let supportedPlatform = target.getSupportedPlatform(for: platform, usingXCTest: target.type == .test) args += [self.triple.tripleString(forPlatformVersion: supportedPlatform.version.versionString)] } else { @@ -158,7 +158,7 @@ extension BuildParameters { /// Returns the scoped view of build settings for a given target. func createScope(for target: ResolvedTarget) -> BuildSettings.Scope { - BuildSettings.Scope(target.underlying.buildSettings, environment: buildEnvironment) + return BuildSettings.Scope(target.underlying.buildSettings, environment: buildEnvironment) } } @@ -185,6 +185,16 @@ public class BuildPlan: SPMBuildCore.BuildPlan { /// Build parameters used for tools. public let toolsBuildParameters: BuildParameters + /// Triple for which this target is compiled. + private func buildTriple(for target: ResolvedTarget) -> Basics.Triple { + self.buildParameters(for: target).triple + } + + /// Triple for which this product is compiled. + private func buildTriple(for product: ResolvedProduct) -> Basics.Triple { + self.buildParameters(for: product).triple + } + /// The package graph. public let graph: PackageGraph @@ -221,14 +231,14 @@ public class BuildPlan: SPMBuildCore.BuildPlan { /// Cache for pkgConfig flags. private var pkgConfigCache = [SystemLibraryTarget: (cFlags: [String], libs: [String])]() - /// Cache for library information. + /// Cache for library information. private var externalLibrariesCache = [BinaryTarget: [LibraryInfo]]() - /// Cache for tools information. + /// Cache for tools information. var externalExecutablesCache = [BinaryTarget: [ExecutableInfo]]() /// Whether to disable sandboxing (e.g. for macros). - private let shouldDisableSandbox: Bool + private let disableSandbox: Bool /// The filesystem to operate on. let fileSystem: any FileSystem @@ -236,7 +246,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan { /// ObservabilityScope with which to emit diagnostics let observabilityScope: ObservabilityScope - @available(*, deprecated, renamed: "init(destinationBuildParameters:toolsBuildParameters:graph:)") + @available(*, deprecated, renamed: "init(productsBuildParameters:toolsBuildParameters:graph:)") public convenience init( buildParameters: BuildParameters, graph: PackageGraph, @@ -247,7 +257,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan { observabilityScope: ObservabilityScope ) throws { try self.init( - destinationBuildParameters: buildParameters, + productsBuildParameters: buildParameters, toolsBuildParameters: buildParameters, graph: graph, additionalFileRules: additionalFileRules, @@ -258,27 +268,9 @@ public class BuildPlan: SPMBuildCore.BuildPlan { ) } - @available(*, deprecated, renamed: "init(destinationBuildParameters:toolsBuildParameters:graph:fileSystem:observabilityScope:)") - public convenience init( - productsBuildParameters: BuildParameters, - toolsBuildParameters: BuildParameters, - graph: PackageGraph, - fileSystem: any FileSystem, - observabilityScope: ObservabilityScope - ) throws { - try self.init( - destinationBuildParameters: productsBuildParameters, - toolsBuildParameters: toolsBuildParameters, - graph: graph, - fileSystem: fileSystem, - observabilityScope: observabilityScope - ) - } - - /// Create a build plan with a package graph and explicitly distinct build parameters for destination platform and - /// tools platform. + /// Create a build plan with a package graph and explicitly distinct build parameters for products and tools. public init( - destinationBuildParameters: BuildParameters, + productsBuildParameters: BuildParameters, toolsBuildParameters: BuildParameters, graph: PackageGraph, additionalFileRules: [FileRuleDescription] = [], @@ -288,17 +280,16 @@ public class BuildPlan: SPMBuildCore.BuildPlan { fileSystem: any FileSystem, observabilityScope: ObservabilityScope ) throws { - self.destinationBuildParameters = destinationBuildParameters + self.destinationBuildParameters = productsBuildParameters self.toolsBuildParameters = toolsBuildParameters self.graph = graph self.buildToolPluginInvocationResults = buildToolPluginInvocationResults self.prebuildCommandResults = prebuildCommandResults - self.shouldDisableSandbox = disableSandbox + self.disableSandbox = disableSandbox self.fileSystem = fileSystem self.observabilityScope = observabilityScope.makeChildScope(description: "Build Plan") - var productMap: [ResolvedProduct.ID: (product: ResolvedProduct, buildDescription: ProductBuildDescription)] = - [:] + var productMap: [ResolvedProduct.ID: (product: ResolvedProduct, buildDescription: ProductBuildDescription)] = [:] // Create product description for each product we have in the package graph that is eligible. for product in graph.allProducts where product.shouldCreateProductDescription { let buildParameters: BuildParameters @@ -306,7 +297,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan { case .tools: buildParameters = toolsBuildParameters case .destination: - buildParameters = destinationBuildParameters + buildParameters = productsBuildParameters } guard let package = graph.package(for: product) else { @@ -344,7 +335,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan { case .tools: buildParameters = toolsBuildParameters case .destination: - buildParameters = destinationBuildParameters + buildParameters = productsBuildParameters } // Validate the product dependencies of this target. @@ -393,13 +384,12 @@ public class BuildPlan: SPMBuildCore.BuildPlan { target: target, toolsVersion: toolsVersion, additionalFileRules: additionalFileRules, - destinationBuildParameters: buildParameters, - toolsBuildParameters: toolsBuildParameters, + buildParameters: buildParameters, buildToolPluginInvocationResults: buildToolPluginInvocationResults[target.id] ?? [], prebuildCommandResults: prebuildCommandResults[target.id] ?? [], requiredMacroProducts: requiredMacroProducts, shouldGenerateTestObservation: generateTestObservation, - shouldDisableSandbox: self.shouldDisableSandbox, + disableSandbox: self.disableSandbox, fileSystem: fileSystem, observabilityScope: observabilityScope ) @@ -446,21 +436,18 @@ public class BuildPlan: SPMBuildCore.BuildPlan { } // Plan the derived test targets, if necessary. - if destinationBuildParameters.testingParameters.testProductStyle.requiresAdditionalDerivedTestTargets { + if productsBuildParameters.testingParameters.testProductStyle.requiresAdditionalDerivedTestTargets { let derivedTestTargets = try Self.makeDerivedTestTargets( - destinationBuildParameters: destinationBuildParameters, - toolsBuildParameters: toolsBuildParameters, + productsBuildParameters, graph, - shouldDisableSandbox: self.shouldDisableSandbox, + self.disableSandbox, self.fileSystem, self.observabilityScope ) for item in derivedTestTargets { var derivedTestTargets = [item.entryPointTargetBuildDescription.target] - targetMap[item.entryPointTargetBuildDescription.target.id] = .swift( - item.entryPointTargetBuildDescription - ) + targetMap[item.entryPointTargetBuildDescription.target.id] = .swift(item.entryPointTargetBuildDescription) if let discoveryTargetBuildDescription = item.discoveryTargetBuildDescription { targetMap[discoveryTargetBuildDescription.target.id] = .swift(discoveryTargetBuildDescription) @@ -566,9 +553,9 @@ public class BuildPlan: SPMBuildCore.BuildPlan { } // Add search paths from the system library targets. - for target in self.graph.reachableTargets { + for target in graph.reachableTargets { if let systemLib = target.underlying as? SystemLibraryTarget { - try arguments.append(contentsOf: self.pkgConfig(for: systemLib).cFlags) + arguments.append(contentsOf: try self.pkgConfig(for: systemLib).cFlags) // Add the path to the module map. arguments += ["-I", systemLib.moduleMapPath.parentDirectory.pathString] } @@ -603,7 +590,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan { } // Add search paths from the system library targets. - for target in self.graph.reachableTargets { + for target in graph.reachableTargets { if let systemLib = target.underlying as? SystemLibraryTarget { arguments += try self.pkgConfig(for: systemLib).cFlags } @@ -742,7 +729,7 @@ extension ResolvedProduct { } private var isBinaryOnly: Bool { - self.targets.filter { !($0.underlying is BinaryTarget) }.isEmpty + return self.targets.filter({ !($0.underlying is BinaryTarget) }).isEmpty } private var isPlugin: Bool { diff --git a/Sources/Build/CMakeLists.txt b/Sources/Build/CMakeLists.txt index d978c3ff53a..fcc51aed76d 100644 --- a/Sources/Build/CMakeLists.txt +++ b/Sources/Build/CMakeLists.txt @@ -10,7 +10,6 @@ add_library(Build BuildDescription/ClangTargetBuildDescription.swift BuildDescription/PluginDescription.swift BuildDescription/ProductBuildDescription.swift - BuildDescription/ResolvedTarget+BuildDescription.swift BuildDescription/SwiftTargetBuildDescription.swift BuildDescription/TargetBuildDescription.swift BuildManifest/LLBuildManifestBuilder.swift diff --git a/Sources/Commands/PackageTools/PluginCommand.swift b/Sources/Commands/PackageTools/PluginCommand.swift index c90553d7b1c..1968dae8183 100644 --- a/Sources/Commands/PackageTools/PluginCommand.swift +++ b/Sources/Commands/PackageTools/PluginCommand.swift @@ -14,10 +14,7 @@ import ArgumentParser import Basics import CoreCommands import Dispatch - -@_spi(SwiftPMInternal) import PackageGraph - import PackageModel import enum TSCBasic.ProcessEnv @@ -318,9 +315,6 @@ struct PluginCommand: SwiftCommand { let toolSearchDirs = [try swiftTool.getTargetToolchain().swiftCompilerPath.parentDirectory] + getEnvSearchPaths(pathString: ProcessEnv.path, currentWorkingDirectory: .none) - var buildToolsGraph = packageGraph - try buildToolsGraph.updateBuildTripleRecursively(.tools) - let buildParameters = try swiftTool.toolsBuildParameters // Build or bring up-to-date any executable host-side tools on which this plugin depends. Add them and any binary dependencies to the tool-names-to-path map. let buildSystem = try swiftTool.createBuildSystem( @@ -329,12 +323,10 @@ struct PluginCommand: SwiftCommand { // Force all dependencies to be built for the host, to work around the fact that BuildOperation.plan // knows to compile build tool plugin dependencies for the host but does not do the same for command // plugins. - productsBuildParameters: buildParameters, - packageGraphLoader: { buildToolsGraph } + productsBuildParameters: buildParameters ) - let accessibleTools = try plugin.processAccessibleTools( - packageGraph: buildToolsGraph, + packageGraph: packageGraph, fileSystem: swiftTool.fileSystem, environment: buildParameters.buildEnvironment, for: try pluginScriptRunner.hostTriple diff --git a/Sources/Commands/SwiftTestTool.swift b/Sources/Commands/SwiftTestTool.swift index 63e8fde2305..1a713702f43 100644 --- a/Sources/Commands/SwiftTestTool.swift +++ b/Sources/Commands/SwiftTestTool.swift @@ -421,7 +421,7 @@ public struct SwiftTestTool: SwiftCommand { let toolchain = try swiftTool.getTargetToolchain() let testEnv = try TestingSupport.constructTestEnvironment( toolchain: toolchain, - destinationBuildParameters: buildParameters, + buildParameters: buildParameters, sanitizers: globalOptions.build.sanitizers ) @@ -662,17 +662,13 @@ extension SwiftTestTool { // MARK: - swift-testing private func swiftTestingRun(_ swiftTool: SwiftTool) throws { - let buildParameters = try swiftTool.buildParametersForTest( - enableCodeCoverage: false, - shouldSkipBuilding: sharedOptions.shouldSkipBuilding, - library: .swiftTesting - ) + let buildParameters = try swiftTool.buildParametersForTest(enableCodeCoverage: false, shouldSkipBuilding: sharedOptions.shouldSkipBuilding, library: .swiftTesting) let testProducts = try buildTestsIfNeeded(swiftTool: swiftTool, buildParameters: buildParameters) let toolchain = try swiftTool.getTargetToolchain() let testEnv = try TestingSupport.constructTestEnvironment( toolchain: toolchain, - destinationBuildParameters: buildParameters, + buildParameters: buildParameters, sanitizers: globalOptions.build.sanitizers ) @@ -974,7 +970,7 @@ final class ParallelTestRunner { let testEnv = try TestingSupport.constructTestEnvironment( toolchain: self.toolchain, - destinationBuildParameters: self.buildParameters, + buildParameters: self.buildParameters, sanitizers: self.buildOptions.sanitizers ) diff --git a/Sources/Commands/Utilities/PluginDelegate.swift b/Sources/Commands/Utilities/PluginDelegate.swift index ec1257fcece..d4ad1554444 100644 --- a/Sources/Commands/Utilities/PluginDelegate.swift +++ b/Sources/Commands/Utilities/PluginDelegate.swift @@ -235,7 +235,7 @@ final class PluginDelegate: PluginInvocationDelegate { // Construct the environment we'll pass down to the tests. let testEnvironment = try TestingSupport.constructTestEnvironment( toolchain: toolchain, - destinationBuildParameters: toolsBuildParameters, + buildParameters: toolsBuildParameters, sanitizers: swiftTool.options.build.sanitizers ) diff --git a/Sources/Commands/Utilities/SymbolGraphExtract.swift b/Sources/Commands/Utilities/SymbolGraphExtract.swift index 7bdb96dd907..f9423384c9b 100644 --- a/Sources/Commands/Utilities/SymbolGraphExtract.swift +++ b/Sources/Commands/Utilities/SymbolGraphExtract.swift @@ -66,7 +66,7 @@ public struct SymbolGraphExtract { // Construct arguments for extracting symbols for a single target. var commandLine = [self.tool.pathString] commandLine += ["-module-name", target.c99name] - commandLine += try buildParameters.tripleArgs(for: target) + commandLine += try buildParameters.targetTripleArgs(for: target) commandLine += try buildPlan.createAPIToolCommonArgs(includeLibrarySearchPaths: true) commandLine += ["-module-cache-path", try buildParameters.moduleCache.pathString] if verboseOutput { diff --git a/Sources/Commands/Utilities/TestingSupport.swift b/Sources/Commands/Utilities/TestingSupport.swift index 7556a3e0c56..236feaa072e 100644 --- a/Sources/Commands/Utilities/TestingSupport.swift +++ b/Sources/Commands/Utilities/TestingSupport.swift @@ -36,9 +36,7 @@ enum TestingSupport { func findXCTestHelper(swiftBuildPath: AbsolutePath) -> AbsolutePath? { // XCTestHelper tool is installed in libexec. - let maybePath = swiftBuildPath.parentDirectory.parentDirectory.appending( - components: "libexec", "swift", "pm", "swiftpm-xctest-helper" - ) + let maybePath = swiftBuildPath.parentDirectory.parentDirectory.appending(components: "libexec", "swift", "pm", "swiftpm-xctest-helper") if swiftTool.fileSystem.isFile(maybePath) { return maybePath } else { @@ -48,10 +46,7 @@ enum TestingSupport { } if let firstCLIArgument = CommandLine.arguments.first { - let runningSwiftBuildPath = try AbsolutePath( - validating: firstCLIArgument, - relativeTo: swiftTool.originalWorkingDirectory - ) + let runningSwiftBuildPath = try AbsolutePath(validating: firstCLIArgument, relativeTo: swiftTool.originalWorkingDirectory) if let xctestHelperPath = findXCTestHelper(swiftBuildPath: runningSwiftBuildPath) { return xctestHelperPath } @@ -59,10 +54,7 @@ enum TestingSupport { // This will be true during swiftpm development or when using swift.org toolchains. let xcodePath = try TSCBasic.Process.checkNonZeroExit(args: "/usr/bin/xcode-select", "--print-path").spm_chomp() - let installedSwiftBuildPath = try TSCBasic.Process.checkNonZeroExit( - args: "/usr/bin/xcrun", "--find", "swift-build", - environment: ["DEVELOPER_DIR": xcodePath] - ).spm_chomp() + let installedSwiftBuildPath = try TSCBasic.Process.checkNonZeroExit(args: "/usr/bin/xcrun", "--find", "swift-build", environment: ["DEVELOPER_DIR": xcodePath]).spm_chomp() if let xctestHelperPath = findXCTestHelper(swiftBuildPath: try AbsolutePath(validating: installedSwiftBuildPath)) { return xctestHelperPath } @@ -118,7 +110,7 @@ enum TestingSupport { args = [try Self.xctestHelperPath(swiftTool: swiftTool).pathString, path.pathString, tempFile.path.pathString] let env = try Self.constructTestEnvironment( toolchain: try swiftTool.getTargetToolchain(), - destinationBuildParameters: swiftTool.buildParametersForTest( + buildParameters: swiftTool.buildParametersForTest( enableCodeCoverage: enableCodeCoverage, shouldSkipBuilding: shouldSkipBuilding, experimentalTestOutput: experimentalTestOutput, @@ -134,7 +126,7 @@ enum TestingSupport { #else let env = try Self.constructTestEnvironment( toolchain: try swiftTool.getTargetToolchain(), - destinationBuildParameters: swiftTool.buildParametersForTest( + buildParameters: swiftTool.buildParametersForTest( enableCodeCoverage: enableCodeCoverage, shouldSkipBuilding: shouldSkipBuilding, library: .xctest @@ -151,7 +143,7 @@ enum TestingSupport { /// Creates the environment needed to test related tools. static func constructTestEnvironment( toolchain: UserToolchain, - destinationBuildParameters buildParameters: BuildParameters, + buildParameters: BuildParameters, sanitizers: [Sanitizer] ) throws -> EnvironmentVariables { var env = EnvironmentVariables.process() diff --git a/Sources/Commands/Utilities/XCTEvents.swift b/Sources/Commands/Utilities/XCTEvents.swift index 0ceedfce77f..a264b205e3a 100644 --- a/Sources/Commands/Utilities/XCTEvents.swift +++ b/Sources/Commands/Utilities/XCTEvents.swift @@ -237,12 +237,12 @@ extension TestErrorInfo { extension TestIssue { init(_ issue: XCTIssue) { self.init( - type: .init(defaultBuildParameters: issue.type), + type: .init(destinationBuildParameters: issue.type), compactDescription: issue.compactDescription, detailedDescription: issue.detailedDescription, - associatedError: issue.associatedError.map { .init(defaultBuildParameters: $0) }, - sourceCodeContext: .init(defaultBuildParameters: issue.sourceCodeContext), - attachments: issue.attachments.map { .init(defaultBuildParameters: $0) } + associatedError: issue.associatedError.map { .init(destinationBuildParameters: $0) }, + sourceCodeContext: .init(destinationBuildParameters: issue.sourceCodeContext), + attachments: issue.attachments.map { .init(destinationBuildParameters: $0) } ) } } @@ -275,8 +275,8 @@ extension TestLocation { extension TestSourceCodeContext { init(_ context: XCTSourceCodeContext) { self.init( - callStack: context.callStack.map { .init(defaultBuildParameters: $0) }, - location: context.location.map { .init(defaultBuildParameters: $0) } + callStack: context.callStack.map { .init(destinationBuildParameters: $0) }, + location: context.location.map { .init(destinationBuildParameters: $0) } ) } } @@ -285,8 +285,8 @@ extension TestSourceCodeFrame { init(_ frame: XCTSourceCodeFrame) { self.init( address: frame.address, - symbolInfo: (try? frame.symbolInfo()).map { .init(defaultBuildParameters: $0) }, - symbolicationError: frame.symbolicationError.map { .init(defaultBuildParameters: $0) } + symbolInfo: (try? frame.symbolInfo()).map { .init(destinationBuildParameters: $0) }, + symbolicationError: frame.symbolicationError.map { .init(destinationBuildParameters: $0) } ) } } @@ -296,7 +296,7 @@ extension TestSourceCodeSymbolInfo { self.init( imageName: symbolInfo.imageName, symbolName: symbolInfo.symbolName, - location: symbolInfo.location.map { .init(defaultBuildParameters: $0) } + location: symbolInfo.location.map { .init(destinationBuildParameters: $0) } ) } } diff --git a/Sources/PackageGraph/BuildTriple.swift b/Sources/PackageGraph/BuildTriple.swift index 87d2daf21f1..4e121a2c7bb 100644 --- a/Sources/PackageGraph/BuildTriple.swift +++ b/Sources/PackageGraph/BuildTriple.swift @@ -10,9 +10,6 @@ // //===----------------------------------------------------------------------===// -import class PackageModel.Target -import class PackageModel.Product - /// Triple for which code should be compiled for. /// > Note: We're not using "host" and "target" triple terminology in this enum, as that clashes with build /// > system "targets" and can lead to confusion in this context. @@ -23,23 +20,3 @@ public enum BuildTriple { /// Triple of the destination platform for which end products are compiled (the target triple). case destination } - -extension Target { - var buildTriple: BuildTriple { - if self.type == .macro || self.type == .plugin { - .tools - } else { - .destination - } - } -} - -extension Product { - var buildTriple: BuildTriple { - if self.type == .macro || self.type == .plugin { - .tools - } else { - .destination - } - } -} diff --git a/Sources/PackageGraph/PackageGraph+Loading.swift b/Sources/PackageGraph/PackageGraph+Loading.swift index 2c456468f1d..420e1ccce67 100644 --- a/Sources/PackageGraph/PackageGraph+Loading.swift +++ b/Sources/PackageGraph/PackageGraph+Loading.swift @@ -165,12 +165,12 @@ extension PackageGraph { observabilityScope: observabilityScope ) - let rootPackages = resolvedPackages.filter { root.manifests.values.contains($0.manifest) } + let rootPackages = resolvedPackages.filter{ root.manifests.values.contains($0.manifest) } checkAllDependenciesAreUsed(rootPackages, observabilityScope: observabilityScope) return try PackageGraph( rootPackages: rootPackages, - rootDependencies: resolvedPackages.filter { rootDependencies.contains($0.manifest) }, + rootDependencies: resolvedPackages.filter{ rootDependencies.contains($0.manifest) }, dependencies: requiredDependencies, binaryArtifacts: binaryArtifacts ) @@ -180,16 +180,16 @@ extension PackageGraph { private func checkAllDependenciesAreUsed(_ rootPackages: [ResolvedPackage], observabilityScope: ObservabilityScope) { for package in rootPackages { // List all dependency products dependent on by the package targets. - let productDependencies = IdentifiableSet(package.targets.flatMap { target in - return target.dependencies.compactMap { targetDependency in + let productDependencies = IdentifiableSet(package.targets.flatMap({ target in + return target.dependencies.compactMap({ targetDependency in switch targetDependency { case .product(let product, _): return product case .target: return nil } - } - }) + }) + })) for dependency in package.dependencies { // We continue if the dependency contains executable products to make sure we don't @@ -217,12 +217,7 @@ private func checkAllDependenciesAreUsed(_ rootPackages: [ResolvedPackage], obse ) // Otherwise emit a warning if none of the dependency package's products are used. - let dependencyIsUsed = dependency.products.contains { product in - // Don't compare by product ID, but by product name to make sure both build triples as properties of - // `ResolvedProduct.ID` are allowed. - productDependencies.contains { $0.name == product.name } - } - + let dependencyIsUsed = dependency.products.contains(where: { productDependencies.contains(id: $0.id) }) if !dependencyIsUsed && !observabilityScope.errorsReportedInAnyScope { packageDiagnosticsScope.emit(.unusedDependency(dependency.identity.description)) } @@ -280,10 +275,7 @@ private func createResolvedPackages( // Resolve module aliases, if specified, for targets and their dependencies // across packages. Aliasing will result in target renaming. - let moduleAliasingUsed = try resolveModuleAliases( - packageBuilders: packageBuilders, - observabilityScope: observabilityScope - ) + let moduleAliasingUsed = try resolveModuleAliases(packageBuilders: packageBuilders, observabilityScope: observabilityScope) // Scan and validate the dependencies for packageBuilder in packageBuilders { @@ -635,12 +627,12 @@ private func createResolvedPackages( observabilityScope.emit( ModuleError.duplicateModule( targetName: entry.key, - packages: entry.value.map { $0.identity }) + packages: entry.value.map{ $0.identity }) ) } } - return try packageBuilders.map { try $0.construct() } + return try packageBuilders.map{ try $0.construct() } } private func emitDuplicateProductDiagnostic( diff --git a/Sources/PackageGraph/PackageGraph.swift b/Sources/PackageGraph/PackageGraph.swift index 8a5e8c7426d..bd3446b0499 100644 --- a/Sources/PackageGraph/PackageGraph.swift +++ b/Sources/PackageGraph/PackageGraph.swift @@ -59,16 +59,17 @@ public struct PackageGraph { public let packages: [ResolvedPackage] /// The list of all targets reachable from root targets. - public private(set) var reachableTargets: IdentifiableSet + public let reachableTargets: IdentifiableSet /// The list of all products reachable from root targets. - public private(set) var reachableProducts: IdentifiableSet + public let reachableProducts: IdentifiableSet /// Returns all the targets in the graph, regardless if they are reachable from the root targets or not. - public private(set) var allTargets: IdentifiableSet + public let allTargets: IdentifiableSet /// Returns all the products in the graph, regardless if they are reachable from the root targets or not. - public private(set) var allProducts: IdentifiableSet + + public let allProducts: IdentifiableSet /// Package dependencies required for a fully resolved graph. /// @@ -79,14 +80,10 @@ public struct PackageGraph { /// Returns true if a given target is present in root packages and is not excluded for the given build environment. public func isInRootPackages(_ target: ResolvedTarget, satisfying buildEnvironment: BuildEnvironment) -> Bool { // FIXME: This can be easily cached. - return rootPackages.reduce( - into: IdentifiableSet() - ) { (accumulator: inout IdentifiableSet, package: ResolvedPackage) in + return rootPackages.reduce(into: IdentifiableSet()) { (accumulator: inout IdentifiableSet, package: ResolvedPackage) in let allDependencies = package.targets.flatMap { $0.dependencies } let unsatisfiedDependencies = allDependencies.filter { !$0.satisfies(buildEnvironment) } - let unsatisfiedDependencyTargets = unsatisfiedDependencies.compactMap { ( - dep: ResolvedTarget.Dependency - ) -> ResolvedTarget? in + let unsatisfiedDependencyTargets = unsatisfiedDependencies.compactMap { (dep: ResolvedTarget.Dependency) -> ResolvedTarget? in switch dep { case .target(let target, _): return target @@ -104,14 +101,14 @@ public struct PackageGraph { return self.rootPackages.contains(id: package.id) } - private var targetsToPackages: [ResolvedTarget.ID: ResolvedPackage] + private let targetsToPackages: [ResolvedTarget.ID: ResolvedPackage] /// Returns the package that contains the target, or nil if the target isn't in the graph. public func package(for target: ResolvedTarget) -> ResolvedPackage? { return self.targetsToPackages[target.id] } - private var productsToPackages: [ResolvedProduct.ID: ResolvedPackage] + private let productsToPackages: [ResolvedProduct.ID: ResolvedPackage] /// Returns the package that contains the product, or nil if the product isn't in the graph. public func package(for product: ResolvedProduct) -> ResolvedPackage? { return self.productsToPackages[product.id] @@ -135,68 +132,44 @@ public struct PackageGraph { self.inputPackages = rootPackages + rootDependencies self.binaryArtifacts = binaryArtifacts self.packages = try topologicalSort(inputPackages, successors: { $0.dependencies }) - let identitiesToPackages = self.packages.spm_createDictionary { ($0.identity, $0) } // Create a mapping from targets to the packages that define them. Here // we include all targets, including tests in non-root packages, since // this is intended for lookup and not traversal. - var targetsToPackages = self.packages.reduce(into: [:], { partial, package in - package.targets.forEach { partial[$0.id] = package } - }) - - // Create a mapping from products to the packages that define them. Here - // we include all products, including tests in non-root packages, since - // this is intended for lookup and not traversal. - var productsToPackages = packages.reduce(into: [:], { partial, package in - package.products.forEach { partial[$0.id] = package } + self.targetsToPackages = packages.reduce(into: [:], { partial, package in + package.targets.forEach{ partial[$0.id] = package } }) - var allTargets = IdentifiableSet() - var allProducts = IdentifiableSet() - for package in self.packages { - let targetsToInclude: [ResolvedTarget] + let allTargets = IdentifiableSet(packages.flatMap({ package -> [ResolvedTarget] in if rootPackages.contains(id: package.id) { - targetsToInclude = Array(package.targets) + return package.targets } else { // Don't include tests targets from non-root packages so swift-test doesn't // try to run them. - targetsToInclude = package.targets.filter { $0.type != .test } + return package.targets.filter({ $0.type != .test }) } + })) - for target in targetsToInclude { - allTargets.insert(target) - - // Explicitly include dependencies of host tools in the maps of all targets or all products - if target.buildTriple == .tools { - for dependency in try target.recursiveDependencies() { - switch dependency { - case .target(let targetDependency, _): - allTargets.insert(targetDependency) - targetsToPackages[targetDependency.id] = package - case .product(let productDependency, _): - allProducts.insert(productDependency) - productsToPackages[productDependency.id] = - identitiesToPackages[productDependency.packageIdentity] - } - } - } - } + // Create a mapping from products to the packages that define them. Here + // we include all products, including tests in non-root packages, since + // this is intended for lookup and not traversal. + self.productsToPackages = packages.reduce(into: [:], { partial, package in + package.products.forEach { partial[$0.id] = package } + }) + let allProducts = IdentifiableSet(packages.flatMap({ package -> [ResolvedProduct] in if rootPackages.contains(id: package.id) { - allProducts.formUnion(package.products) + return package.products } else { // Don't include tests products from non-root packages so swift-test doesn't // try to run them. - allProducts.formUnion(package.products.filter { $0.type != .test }) + return package.products.filter({ $0.type != .test }) } - } - - self.targetsToPackages = targetsToPackages - self.productsToPackages = productsToPackages + })) // Compute the reachable targets and products. - let inputTargets = self.inputPackages.flatMap { $0.targets } - let inputProducts = self.inputPackages.flatMap { $0.products } + let inputTargets = inputPackages.flatMap { $0.targets } + let inputProducts = inputPackages.flatMap { $0.products } let recursiveDependencies = try inputTargets.lazy.flatMap { try $0.recursiveDependencies() } self.reachableTargets = IdentifiableSet(inputTargets).union(recursiveDependencies.compactMap { $0.target }) @@ -206,42 +179,6 @@ public struct PackageGraph { self.allProducts = allProducts } - @_spi(SwiftPMInternal) - public mutating func updateBuildTripleRecursively(_ buildTriple: BuildTriple) throws { - self.reachableTargets = IdentifiableSet(self.reachableTargets.map { - var target = $0 - target.buildTriple = buildTriple - return target - }) - self.reachableProducts = IdentifiableSet(self.reachableProducts.map { - var product = $0 - product.buildTriple = buildTriple - return product - }) - - self.allTargets = IdentifiableSet(self.allTargets.map { - var target = $0 - target.buildTriple = buildTriple - return target - }) - self.allProducts = IdentifiableSet(self.allProducts.map { - var product = $0 - product.buildTriple = buildTriple - return product - }) - - self.targetsToPackages = .init(self.targetsToPackages.map { - var target = $0 - target.buildTriple = buildTriple - return (target, $1) - }, uniquingKeysWith: { $1 }) - self.productsToPackages = .init(self.productsToPackages.map { - var product = $0 - product.buildTriple = buildTriple - return (product, $1) - }, uniquingKeysWith: { $1 }) - } - /// Computes a map from each executable target in any of the root packages to the corresponding test targets. func computeTestTargetsForExecutableTargets() throws -> [ResolvedTarget.ID: [ResolvedTarget]] { var result = [ResolvedTarget.ID: [ResolvedTarget]]() diff --git a/Sources/PackageGraph/Resolution/ResolvedPackage.swift b/Sources/PackageGraph/Resolution/ResolvedPackage.swift index e745696036a..bf8d81eac78 100644 --- a/Sources/PackageGraph/Resolution/ResolvedPackage.swift +++ b/Sources/PackageGraph/Resolution/ResolvedPackage.swift @@ -11,9 +11,6 @@ //===----------------------------------------------------------------------===// import Basics - -import struct OrderedCollections.OrderedDictionary - import PackageModel /// A fully resolved package. Contains resolved targets, products and dependencies of the package. @@ -67,43 +64,8 @@ public struct ResolvedPackage { platformVersionProvider: PlatformVersionProvider ) { self.underlying = underlying - - var processedTargets = OrderedDictionary( - uniqueKeysWithValues: targets.map { ($0.id, $0) } - ) - var processedProducts = [ResolvedProduct]() - // Make sure that direct macro dependencies of test products are also built for the target triple. - // Without this workaround, `assertMacroExpansion` in tests can't be built, as it requires macros - // and SwiftSyntax to be built for the target triple: https://github.com/apple/swift-package-manager/pull/7349 - for var product in products { - if product.type == .test { - var targets = IdentifiableSet() - for var target in product.targets { - var dependencies = [ResolvedTarget.Dependency]() - for dependency in target.dependencies { - switch dependency { - case .target(var target, let conditions) where target.type == .macro: - target.buildTriple = .destination - dependencies.append(.target(target, conditions: conditions)) - processedTargets[target.id] = target - case .product(var product, let conditions) where product.type == .macro: - product.buildTriple = .destination - dependencies.append(.product(product, conditions: conditions)) - default: - dependencies.append(dependency) - } - } - target.dependencies = dependencies - targets.insert(target) - } - product.targets = targets - } - - processedProducts.append(product) - } - - self.products = processedProducts - self.targets = Array(processedTargets.values) + self.targets = targets + self.products = products self.dependencies = dependencies self.defaultLocalization = defaultLocalization self.supportedPlatforms = supportedPlatforms diff --git a/Sources/PackageGraph/Resolution/ResolvedProduct.swift b/Sources/PackageGraph/Resolution/ResolvedProduct.swift index c63e86bfabe..1208b5b0d7f 100644 --- a/Sources/PackageGraph/Resolution/ResolvedProduct.swift +++ b/Sources/PackageGraph/Resolution/ResolvedProduct.swift @@ -30,7 +30,7 @@ public struct ResolvedProduct { public let underlying: Product /// The top level targets contained in this product. - public internal(set) var targets: IdentifiableSet + public let targets: IdentifiableSet /// Executable target for test entry point file. public let testEntryPointTarget: ResolvedTarget? @@ -44,11 +44,7 @@ public struct ResolvedProduct { public let platformVersionProvider: PlatformVersionProvider /// Triple for which this resolved product should be compiled for. - public internal(set) var buildTriple: BuildTriple { - didSet { - self.updateBuildTriplesOfDependencies() - } - } + public let buildTriple: BuildTriple /// The main executable target of product. /// @@ -67,11 +63,7 @@ public struct ResolvedProduct { } } - public init( - packageIdentity: PackageIdentity, - product: Product, - targets: IdentifiableSet - ) { + public init(packageIdentity: PackageIdentity, product: Product, targets: IdentifiableSet) { assert(product.targets.count == targets.count && product.targets.map(\.name).sorted() == targets.map(\.name).sorted()) self.packageIdentity = packageIdentity self.underlying = product @@ -105,18 +97,7 @@ public struct ResolvedProduct { ) } - self.buildTriple = product.buildTriple - self.updateBuildTriplesOfDependencies() - } - - private mutating func updateBuildTriplesOfDependencies() { - if case .tools = self.buildTriple { - self.targets = IdentifiableSet(self.targets.map { - var target = $0 - target.buildTriple = .tools - return target - }) - } + self.buildTriple = .destination } /// True if this product contains Swift targets. @@ -185,13 +166,13 @@ extension ResolvedProduct { extension ResolvedProduct: Identifiable { /// Resolved target identity that uniquely identifies it in a resolution graph. public struct ID: Hashable { - public let productName: String + public let targetName: String let packageIdentity: PackageIdentity - public var buildTriple: BuildTriple + public let buildTriple: BuildTriple } public var id: ID { - ID(productName: self.name, packageIdentity: self.packageIdentity, buildTriple: self.buildTriple) + ID(targetName: self.name, packageIdentity: self.packageIdentity, buildTriple: self.buildTriple) } } diff --git a/Sources/PackageGraph/Resolution/ResolvedTarget.swift b/Sources/PackageGraph/Resolution/ResolvedTarget.swift index 69864b21e22..4e9fecaf804 100644 --- a/Sources/PackageGraph/Resolution/ResolvedTarget.swift +++ b/Sources/PackageGraph/Resolution/ResolvedTarget.swift @@ -133,7 +133,7 @@ public struct ResolvedTarget { public let underlying: Target /// The dependencies of this target. - public internal(set) var dependencies: [Dependency] + public let dependencies: [Dependency] /// The default localization for resources. public let defaultLocalization: String? @@ -144,11 +144,7 @@ public struct ResolvedTarget { private let platformVersionProvider: PlatformVersionProvider /// Triple for which this resolved target should be compiled for. - public internal (set) var buildTriple: BuildTriple { - didSet { - self.updateBuildTriplesOfDependencies() - } - } + public let buildTriple: BuildTriple /// Create a resolved target instance. public init( @@ -165,26 +161,7 @@ public struct ResolvedTarget { self.defaultLocalization = defaultLocalization self.supportedPlatforms = supportedPlatforms self.platformVersionProvider = platformVersionProvider - self.buildTriple = underlying.buildTriple - self.updateBuildTriplesOfDependencies() - } - - private mutating func updateBuildTriplesOfDependencies() { - if case .tools = self.buildTriple { - for (i, dependency) in dependencies.enumerated() { - let updatedDependency: Dependency - switch dependency { - case .target(var target, let conditions): - target.buildTriple = .tools - updatedDependency = .target(target, conditions: conditions) - case .product(var product, let conditions): - product.buildTriple = .tools - updatedDependency = .product(product, conditions: conditions) - } - - dependencies[i] = updatedDependency - } - } + self.buildTriple = .destination } public func getSupportedPlatform(for platform: Platform, usingXCTest: Bool) -> SupportedPlatform { @@ -198,7 +175,7 @@ public struct ResolvedTarget { extension ResolvedTarget: CustomStringConvertible { public var description: String { - return "" + return "" } } @@ -267,7 +244,7 @@ extension ResolvedTarget: Identifiable { public struct ID: Hashable { public let targetName: String let packageIdentity: PackageIdentity - public var buildTriple: BuildTriple + public let buildTriple: BuildTriple } public var id: ID { diff --git a/Sources/PackageModel/Target/Target.swift b/Sources/PackageModel/Target/Target.swift index 411415a719f..541e37794de 100644 --- a/Sources/PackageModel/Target/Target.swift +++ b/Sources/PackageModel/Target/Target.swift @@ -131,7 +131,7 @@ public class Target: PolymorphicCodableProtocol { /// The name of the target. /// /// NOTE: This name is not the language-level target (i.e., the importable - /// name) name in many cases, instead use ``Target/c99name`` if you need uniqueness. + /// name) name in many cases, instead use c99name if you need uniqueness. public private(set) var name: String /// Module aliases needed to build this target. The key is an original name of a diff --git a/Sources/SPMBuildCore/BuildParameters/BuildParameters.swift b/Sources/SPMBuildCore/BuildParameters/BuildParameters.swift index 6e4ceb04aa2..cbc5cb5499c 100644 --- a/Sources/SPMBuildCore/BuildParameters/BuildParameters.swift +++ b/Sources/SPMBuildCore/BuildParameters/BuildParameters.swift @@ -248,7 +248,7 @@ public struct BuildParameters: Encodable { /// Returns the path to the binary of a product for the current build parameters, relative to the build directory. public func binaryRelativePath(for product: ResolvedProduct) throws -> RelativePath { - let potentialExecutablePath = try RelativePath(validating: "\(product.name)\(product.buildTriple.suffix)\(self.triple.executableExtension)") + let potentialExecutablePath = try RelativePath(validating: "\(product.name)\(self.triple.executableExtension)") switch product.type { case .executable, .snippet: @@ -329,12 +329,3 @@ extension Triple { return !self.isWindows() } } - -extension BuildTriple { - /// Suffix appended to build manifest nodes to distinguish nodes created for tools from nodes created for - /// end products, i.e. nodes for host vs target triples. - @_spi(SwiftPMInternal) - public var suffix: String { - if self == .tools { "-tool" } else { "" } - } -} diff --git a/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift b/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift index 5d9e1e0e982..e8d1fd3efe1 100644 --- a/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift +++ b/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift @@ -74,7 +74,7 @@ extension ProductBuildDescription { /// The path to the product binary produced. public var binaryPath: AbsolutePath { get throws { - try self.buildParameters.binaryPath(for: product) + return try self.buildParameters.binaryPath(for: product) } } } diff --git a/Sources/SPMBuildCore/Plugins/PluginInvocation.swift b/Sources/SPMBuildCore/Plugins/PluginInvocation.swift index f948995f7e6..72dcef42431 100644 --- a/Sources/SPMBuildCore/Plugins/PluginInvocation.swift +++ b/Sources/SPMBuildCore/Plugins/PluginInvocation.swift @@ -592,9 +592,7 @@ extension PackageGraph { } // Associate the list of results with the target. The list will have one entry for each plugin used by the target. - var targetID = target.id - targetID.buildTriple = .destination - pluginResultsByTarget[targetID] = (target, buildToolPluginResults) + pluginResultsByTarget[target.id] = (target, buildToolPluginResults) } return pluginResultsByTarget } diff --git a/Sources/SPMTestSupport/MockBuildTestHelper.swift b/Sources/SPMTestSupport/MockBuildTestHelper.swift index 58297fdd979..2aeb77764ff 100644 --- a/Sources/SPMTestSupport/MockBuildTestHelper.swift +++ b/Sources/SPMTestSupport/MockBuildTestHelper.swift @@ -15,8 +15,6 @@ import Basics @_spi(SwiftPMInternal) import Build -import struct PackageGraph.ResolvedTarget -import struct PackageGraph.ResolvedProduct import PackageModel import SPMBuildCore import TSCUtility @@ -75,14 +73,14 @@ public let defaultTargetTriple: String = hostTriple.tripleString #endif public func mockBuildParameters( - buildPath: AbsolutePath? = nil, + buildPath: AbsolutePath = "/path/to/build", config: BuildConfiguration = .debug, toolchain: PackageModel.Toolchain = MockToolchain(), flags: PackageModel.BuildFlags = PackageModel.BuildFlags(), shouldLinkStaticSwiftStdlib: Bool = false, shouldDisableLocalRpath: Bool = false, canRenameEntrypointFunctionName: Bool = false, - triple: Basics.Triple = hostTriple, + targetTriple: Basics.Triple = hostTriple, indexStoreMode: BuildParameters.IndexStoreMode = .off, useExplicitModuleBuild: Bool = false, linkerDeadStrip: Bool = true, @@ -90,16 +88,16 @@ public func mockBuildParameters( omitFramePointers: Bool? = nil ) -> BuildParameters { try! BuildParameters( - dataPath: buildPath ?? AbsolutePath("/path/to/build").appending(triple.tripleString), + dataPath: buildPath, configuration: config, toolchain: toolchain, - triple: triple, + triple: targetTriple, flags: flags, pkgConfigDirectories: [], workers: 3, indexStoreMode: indexStoreMode, debuggingParameters: .init( - triple: triple, + triple: targetTriple, shouldEnableDebuggingEntitlement: config == .debug, omitFramePointers: omitFramePointers ), @@ -131,7 +129,7 @@ public func mockBuildParameters(environment: BuildEnvironment) -> BuildParameter fatalError("unsupported platform in tests") } - return mockBuildParameters(config: environment.configuration ?? .debug, triple: triple) + return mockBuildParameters(config: environment.configuration ?? .debug, targetTriple: triple) } enum BuildError: Swift.Error { @@ -140,15 +138,15 @@ enum BuildError: Swift.Error { public struct BuildPlanResult { public let plan: Build.BuildPlan - public let targetMap: [ResolvedTarget.ID: TargetBuildDescription] - public let productMap: [ResolvedProduct.ID: Build.ProductBuildDescription] + public let targetMap: [String: TargetBuildDescription] + public let productMap: [String: Build.ProductBuildDescription] public init(plan: Build.BuildPlan) throws { self.plan = plan self.productMap = try Dictionary( throwingUniqueKeysWithValues: plan.buildProducts .compactMap { $0 as? Build.ProductBuildDescription } - .map { ($0.product.id, $0) } + .map { ($0.product.name, $0) } ) self.targetMap = try Dictionary( throwingUniqueKeysWithValues: plan.targetMap.compactMap { @@ -158,7 +156,7 @@ public struct BuildPlanResult { else { throw BuildError.error("Target \($0) not found.") } - return (target.id, $1) + return (target.name, $1) } ) } @@ -172,26 +170,16 @@ public struct BuildPlanResult { } public func target(for name: String) throws -> TargetBuildDescription { - let matchingIDs = targetMap.keys.filter({ $0.targetName == name }) - guard matchingIDs.count == 1, let target = targetMap[matchingIDs[0]] else { - if matchingIDs.isEmpty { - throw BuildError.error("Target \(name) not found.") - } else { - throw BuildError.error("More than one target \(name) found.") - } + guard let target = targetMap[name] else { + throw BuildError.error("Target \(name) not found.") } return target } public func buildProduct(for name: String) throws -> Build.ProductBuildDescription { - let matchingIDs = productMap.keys.filter({ $0.productName == name }) - guard matchingIDs.count == 1, let product = productMap[matchingIDs[0]] else { - if matchingIDs.isEmpty { - // Display the thrown error on macOS - throw BuildError.error("Product \(name) not found.") - } else { - throw BuildError.error("More than one target \(name) found.") - } + guard let product = productMap[name] else { + // Display the thrown error on macOS + throw BuildError.error("Product \(name) not found.") } return product } diff --git a/Sources/SPMTestSupport/MockPackageGraphs.swift b/Sources/SPMTestSupport/MockPackageGraphs.swift deleted file mode 100644 index b84b897a257..00000000000 --- a/Sources/SPMTestSupport/MockPackageGraphs.swift +++ /dev/null @@ -1,122 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift open source project -// -// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See http://swift.org/LICENSE.txt for license information -// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import class Basics.ObservabilitySystem -import class Basics.ObservabilityScope -import struct PackageGraph.PackageGraph -import class PackageModel.Manifest -import struct PackageModel.ProductDescription -import struct PackageModel.TargetDescription -import protocol TSCBasic.FileSystem -import class TSCBasic.InMemoryFileSystem - -public func macrosPackageGraph() throws -> ( - graph: PackageGraph, - fileSystem: FileSystem, - observabilityScope: ObservabilityScope -) { - let fs = InMemoryFileSystem(emptyFiles: - "/swift-firmware/Sources/Core/source.swift", - "/swift-firmware/Sources/HAL/source.swift", - "/swift-firmware/Tests/CoreTests/source.swift", - "/swift-firmware/Tests/HALTests/source.swift", - "/swift-mmio/Sources/MMIO/source.swift", - "/swift-mmio/Sources/MMIOMacros/source.swift", - "/swift-syntax/Sources/SwiftSyntax/source.swift", - "/swift-syntax/Tests/SwiftSyntaxTests/source.swift" - ) - - let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( - fileSystem: fs, - manifests: [ - Manifest.createRootManifest( - displayName: "swift-firmware", - path: "/swift-firmware", - dependencies: [ - .localSourceControl( - path: "/swift-mmio", - requirement: .upToNextMajor(from: "1.0.0") - ) - ], - products: [ - ProductDescription( - name: "Core", - type: .executable, - targets: ["Core"] - ) - ], - targets: [ - TargetDescription( - name: "Core", - dependencies: ["HAL"], - type: .executable - ), - TargetDescription( - name: "HAL", - dependencies: [.product(name: "MMIO", package: "swift-mmio")] - ), - TargetDescription(name: "CoreTests", dependencies: ["Core"], type: .test), - TargetDescription(name: "HALTests", dependencies: ["HAL"], type: .test), - ] - ), - Manifest.createFileSystemManifest( - displayName: "swift-mmio", - path: "/swift-mmio", - dependencies: [ - .localSourceControl( - path: "/swift-syntax", - requirement: .upToNextMajor(from: "1.0.0") - ) - ], - products: [ - ProductDescription( - name: "MMIO", - type: .library(.automatic), - targets: ["MMIO"] - ) - ], - targets: [ - TargetDescription( - name: "MMIO", - dependencies: [.target(name: "MMIOMacros")] - ), - TargetDescription( - name: "MMIOMacros", - dependencies: [.product(name: "SwiftSyntax", package: "swift-syntax")], - type: .macro - ) - ] - ), - Manifest.createFileSystemManifest( - displayName: "swift-syntax", - path: "/swift-syntax", - products: [ - ProductDescription( - name: "SwiftSyntax", - type: .library(.automatic), - targets: ["SwiftSyntax"] - ) - ], - targets: [ - TargetDescription(name: "SwiftSyntax", dependencies: []), - TargetDescription(name: "SwiftSyntaxTests", dependencies: ["SwiftSyntax"], type: .test), - ] - ), - ], - observabilityScope: observability.topScope - ) - - XCTAssertNoDiagnostics(observability.diagnostics) - - return (graph, fs, observability.topScope) -} diff --git a/Sources/SPMTestSupport/PackageGraphTester.swift b/Sources/SPMTestSupport/PackageGraphTester.swift index 91d2d61d8e3..fc4d3c231b5 100644 --- a/Sources/SPMTestSupport/PackageGraphTester.swift +++ b/Sources/SPMTestSupport/PackageGraphTester.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors +// Copyright (c) 2014-2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information @@ -98,15 +98,6 @@ public final class PackageGraphResult { body(ResolvedTargetResult(target)) } - public func checkTargets( - _ name: String, - file: StaticString = #file, - line: UInt = #line, - body: ([ResolvedTargetResult]) -> Void - ) { - body(graph.allTargets.filter { $0.name == name }.map(ResolvedTargetResult.init)) - } - public func checkProduct( _ name: String, file: StaticString = #file, @@ -158,7 +149,7 @@ public final class PackageGraphResult { } public final class ResolvedTargetResult { - let target: ResolvedTarget + private let target: ResolvedTarget init(_ target: ResolvedTarget) { self.target = target @@ -185,9 +176,7 @@ public final class ResolvedTargetResult { } public func checkDeclaredPlatforms(_ platforms: [String: String], file: StaticString = #file, line: UInt = #line) { - let targetPlatforms = Dictionary( - uniqueKeysWithValues: target.supportedPlatforms.map { ($0.platform.name, $0.version.versionString) } - ) + let targetPlatforms = Dictionary(uniqueKeysWithValues: target.supportedPlatforms.map({ ($0.platform.name, $0.version.versionString) })) XCTAssertEqual(platforms, targetPlatforms, file: file, line: line) } @@ -198,24 +187,16 @@ public final class ResolvedTargetResult { return self.target.getSupportedPlatform(for: platform, usingXCTest: self.target.type == .test) } let targetPlatforms = Dictionary( - uniqueKeysWithValues: derived.map { ($0.platform.name, $0.version.versionString) } + uniqueKeysWithValues: derived + .map { ($0.platform.name, $0.version.versionString) } ) XCTAssertEqual(platforms, targetPlatforms, file: file, line: line) } - public func checkDerivedPlatformOptions( - _ platform: PackageModel.Platform, - options: [String], - file: StaticString = #file, - line: UInt = #line - ) { - let platform = self.target.getSupportedPlatform(for: platform, usingXCTest: target.type == .test) + public func checkDerivedPlatformOptions(_ platform: PackageModel.Platform, options: [String], file: StaticString = #file, line: UInt = #line) { + let platform = target.getSupportedPlatform(for: platform, usingXCTest: target.type == .test) XCTAssertEqual(platform.options, options, file: file, line: line) } - - public func check(buildTriple: BuildTriple, file: StaticString = #file, line: UInt = #line) { - XCTAssertEqual(self.target.buildTriple, buildTriple, file: file, line: line) - } } public final class ResolvedTargetDependencyResult { @@ -236,28 +217,6 @@ public final class ResolvedTargetDependencyResult { ) { XCTAssert(!dependency.conditions.allSatisfy({ $0.satisfies(environment) }), file: file, line: line) } - - public func checkTarget( - file: StaticString = #file, - line: UInt = #line, - body: (ResolvedTargetResult) -> Void - ) { - guard case let .target(target, _) = self.dependency else { - return XCTFail("Dependency \(dependency) is not a target", file: file, line: line) - } - body(ResolvedTargetResult(target)) - } - - public func checkProduct( - file: StaticString = #file, - line: UInt = #line, - body: (ResolvedProductResult) -> Void - ) { - guard case let .product(product, _) = self.dependency else { - return XCTFail("Dependency \(dependency) is not a product", file: file, line: line) - } - body(ResolvedProductResult(product)) - } } public final class ResolvedProductResult { @@ -293,22 +252,6 @@ public final class ResolvedProductResult { let platform = product.getSupportedPlatform(for: platform, usingXCTest: product.isLinkingXCTest) XCTAssertEqual(platform.options, options, file: file, line: line) } - - public func check(buildTriple: BuildTriple, file: StaticString = #file, line: UInt = #line) { - XCTAssertEqual(self.product.buildTriple, buildTriple, file: file, line: line) - } - - public func checkTarget( - _ name: String, - file: StaticString = #file, - line: UInt = #line, - body: (ResolvedTargetResult) -> Void - ) { - guard let target = product.targets.first(where: { $0.name == name }) else { - return XCTFail("Target \(name) not found", file: file, line: line) - } - body(ResolvedTargetResult(target)) - } } extension ResolvedTarget.Dependency { diff --git a/Tests/BuildTests/BuildOperationTests.swift b/Tests/BuildTests/BuildOperationTests.swift index 6a703ded853..3f196dd41fa 100644 --- a/Tests/BuildTests/BuildOperationTests.swift +++ b/Tests/BuildTests/BuildOperationTests.swift @@ -22,16 +22,14 @@ import class TSCBasic.InMemoryFileSystem final class BuildOperationTests: XCTestCase { func testDetectUnexpressedDependencies() throws { - let buildParameters = mockBuildParameters(shouldDisableLocalRpath: false) - let fs = InMemoryFileSystem(files: [ - "\(buildParameters.dataPath)/debug/Lunch.build/Lunch.d" : "/Best.framework" + "/path/to/build/debug/Lunch.build/Lunch.d" : "/Best.framework" ]) let observability = ObservabilitySystem.makeForTesting() let buildOp = BuildOperation( - productsBuildParameters: buildParameters, - toolsBuildParameters: buildParameters, + productsBuildParameters: mockBuildParameters(shouldDisableLocalRpath: false), + toolsBuildParameters: mockBuildParameters(shouldDisableLocalRpath: false), cacheBuildManifest: false, packageGraphLoader: { fatalError() }, additionalFileRules: [], diff --git a/Tests/BuildTests/BuildPlanTests.swift b/Tests/BuildTests/BuildPlanTests.swift index 5ea4e286dac..2055e433f4b 100644 --- a/Tests/BuildTests/BuildPlanTests.swift +++ b/Tests/BuildTests/BuildPlanTests.swift @@ -47,7 +47,7 @@ extension Build.BuildPlan { observabilityScope: ObservabilityScope ) throws { try self.init( - destinationBuildParameters: buildParameters, + productsBuildParameters: buildParameters, toolsBuildParameters: buildParameters, graph: graph, additionalFileRules: additionalFileRules, @@ -888,7 +888,7 @@ final class BuildPlanTests: XCTestCase { buildPath: buildDirPath, config: .release, toolchain: UserToolchain.default, - triple: UserToolchain.default.targetTriple, + targetTriple: UserToolchain.default.targetTriple, useExplicitModuleBuild: true ), graph: graph, @@ -1118,11 +1118,11 @@ final class BuildPlanTests: XCTestCase { observabilityScope: observability.topScope )) - XCTAssertEqual(Set(result.productMap.keys.map(\.productName)), ["APackageTests"]) + XCTAssertEqual(Set(result.productMap.keys), ["APackageTests"]) #if os(macOS) - XCTAssertEqual(Set(result.targetMap.keys.map(\.targetName)), ["ATarget", "BTarget", "ATargetTests"]) + XCTAssertEqual(Set(result.targetMap.keys), ["ATarget", "BTarget", "ATargetTests"]) #else - XCTAssertEqual(Set(result.targetMap.keys.map(\.targetName)), [ + XCTAssertEqual(Set(result.targetMap.keys), [ "APackageTests", "APackageDiscoveredTests", "ATarget", @@ -1469,13 +1469,7 @@ final class BuildPlanTests: XCTestCase { ]) #endif - let buildProduct = try XCTUnwrap( - result.productMap[.init( - productName: "exe", - packageIdentity: "Pkg", - buildTriple: .destination - )] - ) + let buildProduct = try XCTUnwrap(result.productMap["exe"]) XCTAssertEqual(Array(buildProduct.objects), [ buildPath.appending(components: "exe.build", "main.c.o"), buildPath.appending(components: "extlib.build", "extlib.c.o"), @@ -1718,9 +1712,8 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let buildParameters = mockBuildParameters() let plan = try BuildPlan( - buildParameters: buildParameters, + buildParameters: mockBuildParameters(), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -1791,7 +1784,7 @@ final class BuildPlanTests: XCTestCase { "@\(buildPath.appending(components: "exe.product", "Objects.LinkFileList"))", "-Xlinker", "-rpath", "-Xlinker", "/fake/path/lib/swift-5.5/macosx", "-target", defaultTargetTriple, - "-Xlinker", "-add_ast_path", "-Xlinker", "/path/to/build/\(buildParameters.triple)/debug/exe.build/exe.swiftmodule", + "-Xlinker", "-add_ast_path", "-Xlinker", "/path/to/build/debug/exe.build/exe.swiftmodule", "-g", ]) #elseif os(Windows) @@ -1847,9 +1840,8 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let buildParameters = mockBuildParameters() let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: buildParameters, + buildParameters: mockBuildParameters(), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -1859,8 +1851,8 @@ final class BuildPlanTests: XCTestCase { let lib = try result.target(for: "lib").clangTarget() XCTAssertEqual(try lib.objects, [ - AbsolutePath("/path/to/build/\(buildParameters.triple)/debug/lib.build/lib.S.o"), - AbsolutePath("/path/to/build/\(buildParameters.triple)/debug/lib.build/lib.c.o"), + AbsolutePath("/path/to/build/debug/lib.build/lib.S.o"), + AbsolutePath("/path/to/build/debug/lib.build/lib.c.o"), ]) } @@ -2575,7 +2567,7 @@ final class BuildPlanTests: XCTestCase { // Verify that `-lstdc++` is passed instead of `-lc++` when cross-compiling to Linux. result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(triple: .arm64Linux), + buildParameters: mockBuildParameters(targetTriple: .arm64Linux), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -3381,7 +3373,7 @@ final class BuildPlanTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(triple: .windows), + buildParameters: mockBuildParameters(targetTriple: .windows), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -3468,7 +3460,7 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - var parameters = mockBuildParameters(triple: .wasi) + var parameters = mockBuildParameters(targetTriple: .wasi) parameters.linkingParameters.shouldLinkStaticSwiftStdlib = true let result = try BuildPlanResult(plan: BuildPlan( buildParameters: parameters, @@ -3570,7 +3562,7 @@ final class BuildPlanTests: XCTestCase { try BuildPlanResult(plan: BuildPlan( buildParameters: mockBuildParameters( canRenameEntrypointFunctionName: true, - triple: triple + targetTriple: triple ), graph: graph, fileSystem: fs, @@ -3767,7 +3759,7 @@ final class BuildPlanTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(triple: .init("arm64-apple-ios")), + buildParameters: mockBuildParameters(targetTriple: .init("arm64-apple-ios")), graph: graph, fileSystem: fileSystem, observabilityScope: observability.topScope @@ -3844,7 +3836,7 @@ final class BuildPlanTests: XCTestCase { // constraints above are valid. XCTAssertNoThrow( _ = try BuildPlan( - buildParameters: mockBuildParameters(triple: .arm64iOS), + buildParameters: mockBuildParameters(targetTriple: .arm64iOS), graph: graph, fileSystem: fileSystem, observabilityScope: observability.topScope @@ -3854,7 +3846,7 @@ final class BuildPlanTests: XCTestCase { // For completeness, the invalid target should still throw an error. XCTAssertThrows(Diagnostics.fatalError) { _ = try BuildPlan( - buildParameters: mockBuildParameters(triple: .x86_64MacOS), + buildParameters: mockBuildParameters(targetTriple: .x86_64MacOS), graph: graph, fileSystem: fileSystem, observabilityScope: observability.topScope @@ -3917,7 +3909,7 @@ final class BuildPlanTests: XCTestCase { XCTAssertThrows(Diagnostics.fatalError) { _ = try BuildPlan( - buildParameters: mockBuildParameters(triple: .x86_64MacOS), + buildParameters: mockBuildParameters(targetTriple: .x86_64MacOS), graph: graph, fileSystem: fileSystem, observabilityScope: observability.topScope @@ -4071,7 +4063,7 @@ final class BuildPlanTests: XCTestCase { func createResult(for dest: Basics.Triple) throws -> BuildPlanResult { try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(triple: dest), + buildParameters: mockBuildParameters(targetTriple: dest), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -4134,7 +4126,7 @@ final class BuildPlanTests: XCTestCase { do { let result = try BuildPlanResult(plan: BuildPlan( buildParameters: mockBuildParameters( - triple: .x86_64Linux, + targetTriple: .x86_64Linux, omitFramePointers: true ), graph: graph, @@ -4190,7 +4182,7 @@ final class BuildPlanTests: XCTestCase { do { let result = try BuildPlanResult(plan: BuildPlan( buildParameters: mockBuildParameters( - triple: .x86_64Linux, + targetTriple: .x86_64Linux, omitFramePointers: false ), graph: graph, @@ -4551,7 +4543,7 @@ final class BuildPlanTests: XCTestCase { swiftCompilerFlags: [cliFlag(tool: .swiftCompiler)], linkerFlags: [cliFlag(tool: .linker)] ), - triple: targetTriple + targetTriple: targetTriple ) let result = try BuildPlanResult(plan: BuildPlan( buildParameters: buildParameters, @@ -4836,9 +4828,8 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let buildParameters = mockBuildParameters() let plan = try BuildPlan( - buildParameters: buildParameters, + buildParameters: mockBuildParameters(), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -4854,7 +4845,7 @@ final class BuildPlanTests: XCTestCase { [ .anySequence, "-emit-objc-header", - "-emit-objc-header-path", "/path/to/build/\(buildParameters.triple)/debug/Foo.build/Foo-Swift.h", + "-emit-objc-header-path", "/path/to/build/debug/Foo.build/Foo-Swift.h", .anySequence, ] ) @@ -4864,7 +4855,7 @@ final class BuildPlanTests: XCTestCase { [ .anySequence, "-emit-objc-header", - "-emit-objc-header-path", "/path/to/build/\(buildParameters.triple)/Foo.build/Foo-Swift.h", + "-emit-objc-header-path", "/path/to/build/debug/Foo.build/Foo-Swift.h", .anySequence, ] ) @@ -4874,12 +4865,12 @@ final class BuildPlanTests: XCTestCase { #if os(macOS) XCTAssertMatch( barTarget, - [.anySequence, "-fmodule-map-file=/path/to/build/\(buildParameters.triple)/debug/Foo.build/module.modulemap", .anySequence] + [.anySequence, "-fmodule-map-file=/path/to/build/debug/Foo.build/module.modulemap", .anySequence] ) #else XCTAssertNoMatch( barTarget, - [.anySequence, "-fmodule-map-file=/path/to/build/\(buildParameters.triple)/debug/Foo.build/module.modulemap", .anySequence] + [.anySequence, "-fmodule-map-file=/path/to/build/debug/Foo.build/module.modulemap", .anySequence] ) #endif @@ -4937,9 +4928,8 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let buildParameters = mockBuildParameters() let plan = try BuildPlan( - buildParameters: buildParameters, + buildParameters: mockBuildParameters(), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -4956,7 +4946,7 @@ final class BuildPlanTests: XCTestCase { .anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/\(buildParameters.triple)/debug/Foo.build/Foo-Swift.h", + "/path/to/build/debug/Foo.build/Foo-Swift.h", .anySequence, ] ) @@ -4967,7 +4957,7 @@ final class BuildPlanTests: XCTestCase { .anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/\(buildParameters.triple)/debug/Foo.build/Foo-Swift.h", + "/path/to/build/debug/Foo.build/Foo-Swift.h", .anySequence, ] ) @@ -4979,7 +4969,7 @@ final class BuildPlanTests: XCTestCase { barTarget, [ .anySequence, - "-fmodule-map-file=/path/to/build/\(buildParameters.triple)/debug/Foo.build/module.modulemap", + "-fmodule-map-file=/path/to/build/debug/Foo.build/module.modulemap", .anySequence, ] ) @@ -4988,7 +4978,7 @@ final class BuildPlanTests: XCTestCase { barTarget, [ .anySequence, - "-fmodule-map-file=/path/to/build/\(buildParameters.triple)/debug/Foo.build/module.modulemap", + "-fmodule-map-file=/path/to/build/debug/Foo.build/module.modulemap", .anySequence, ] ) @@ -5048,9 +5038,8 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let buildParameters = mockBuildParameters() let plan = try BuildPlan( - buildParameters: buildParameters, + buildParameters: mockBuildParameters(), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -5071,7 +5060,7 @@ final class BuildPlanTests: XCTestCase { .anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/\(buildParameters.triple)/debug/Foo.build/Foo-Swift.h", + "/path/to/build/debug/Foo.build/Foo-Swift.h", .anySequence, ] ) @@ -5082,7 +5071,7 @@ final class BuildPlanTests: XCTestCase { .anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/\(buildParameters.triple)/debug/Foo.build/Foo-Swift.h", + "/path/to/build/debug/Foo.build/Foo-Swift.h", .anySequence, ] ) @@ -5094,7 +5083,7 @@ final class BuildPlanTests: XCTestCase { barTarget, [ .anySequence, - "-fmodule-map-file=/path/to/build/\(buildParameters.triple)/debug/Foo.build/module.modulemap", + "-fmodule-map-file=/path/to/build/debug/Foo.build/module.modulemap", .anySequence, ] ) @@ -5103,7 +5092,7 @@ final class BuildPlanTests: XCTestCase { barTarget, [ .anySequence, - "-fmodule-map-file=/path/to/build/\(buildParameters.triple)/debug/Foo.build/module.modulemap", + "-fmodule-map-file=/path/to/build/debug/Foo.build/module.modulemap", .anySequence, ] ) @@ -5153,7 +5142,7 @@ final class BuildPlanTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(triple: .x86_64Linux), + buildParameters: mockBuildParameters(targetTriple: .x86_64Linux), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -5457,7 +5446,7 @@ final class BuildPlanTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) let plan = try BuildPlan( - buildParameters: mockBuildParameters(triple: .wasi), + buildParameters: mockBuildParameters(targetTriple: .wasi), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -5590,7 +5579,7 @@ final class BuildPlanTests: XCTestCase { let supportingTriples: [Basics.Triple] = [.x86_64Linux, .arm64Linux, .wasi] for triple in supportingTriples { let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true, triple: triple), + buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true, targetTriple: triple), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -5715,7 +5704,7 @@ final class BuildPlanTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(triple: targetTriple), + buildParameters: mockBuildParameters(targetTriple: targetTriple), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -5844,7 +5833,7 @@ final class BuildPlanTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(triple: targetTriple), + buildParameters: mockBuildParameters(targetTriple: targetTriple), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -6117,13 +6106,7 @@ final class BuildPlanTests: XCTestCase { observabilityScope: observability.topScope )) - switch try XCTUnwrap( - result.targetMap[.init( - targetName: "ExtLib", - packageIdentity: "ExtPkg", - buildTriple: .destination - )] - ) { + switch try XCTUnwrap(result.targetMap["ExtLib"]) { case .swift(let swiftTarget): if #available(macOS 13, *) { // `.contains` is only available in macOS 13 or newer XCTAssertTrue(try swiftTarget.compileArguments().contains(["-user-module-version", "1.0.0"])) @@ -6280,13 +6263,7 @@ final class BuildPlanTests: XCTestCase { result.checkTargetsCount(3) XCTAssertTrue(result.targetMap.values.contains { $0.target.name == "FooLogging" }) XCTAssertTrue(result.targetMap.values.contains { $0.target.name == "BarLogging" }) - let buildProduct = try XCTUnwrap( - result.productMap[.init( - productName: "exe", - packageIdentity: "thisPkg", - buildTriple: .destination - )] - ) + let buildProduct = try XCTUnwrap(result.productMap["exe"]) let dylibs = Array(buildProduct.dylibs.map({$0.product.name})).sorted() XCTAssertEqual(dylibs, ["BarLogging", "FooLogging"]) } diff --git a/Tests/BuildTests/CrossCompilationBuildTests.swift b/Tests/BuildTests/CrossCompilationBuildTests.swift deleted file mode 100644 index f8a9ad600c0..00000000000 --- a/Tests/BuildTests/CrossCompilationBuildTests.swift +++ /dev/null @@ -1,86 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift open source project -// -// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See http://swift.org/LICENSE.txt for license information -// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import class Basics.ObservabilitySystem -import struct Basics.Triple -import class Build.BuildPlan -import class Build.ProductBuildDescription -import class Build.SwiftTargetBuildDescription -import func SPMTestSupport.macrosPackageGraph -import func SPMTestSupport.mockBuildParameters -import struct SPMTestSupport.BuildPlanResult -import func SPMTestSupport.XCTAssertMatch -import class TSCBasic.InMemoryFileSystem - -import XCTest - -extension BuildPlanResult { - func allTargets(named name: String) throws -> [SwiftTargetBuildDescription] { - try self.targetMap - .filter { $0.0.targetName == name } - .map { try $1.swiftTarget() } - } - - func allProducts(named name: String) -> [ProductBuildDescription] { - self.productMap - .filter { $0.0.productName == name } - .map { $1 } - } - - func check(triple: Triple, for target: String, file: StaticString = #file, line: UInt = #line) throws { - let target = try self.target(for: target).swiftTarget() - XCTAssertMatch(try target.emitCommandLine(), [.contains(triple.tripleString)], file: file, line: line) - } -} - -final class CrossCompilationBuildPlanTests: XCTestCase { - func testMacros() throws { - let (graph, fs, scope) = try macrosPackageGraph() - - let productsTriple = Triple.arm64Linux - let toolsTriple = Triple.x86_64MacOS - let plan = try BuildPlan( - destinationBuildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true, triple: productsTriple), - toolsBuildParameters: mockBuildParameters(triple: toolsTriple), - graph: graph, - fileSystem: fs, - observabilityScope: scope - ) - let result = try BuildPlanResult(plan: plan) - result.checkProductsCount(3) - result.checkTargetsCount(10) - - XCTAssertTrue(try result.allTargets(named: "SwiftSyntax").contains { $0.target.buildTriple == .tools }) - try result.check(triple: toolsTriple, for: "MMIOMacros") - try result.check(triple: productsTriple, for: "MMIO") - try result.check(triple: productsTriple, for: "Core") - try result.check(triple: productsTriple, for: "HAL") - - let macroProducts = result.allProducts(named: "MMIOMacros") - XCTAssertEqual(macroProducts.count, 1) - let macroProduct = try XCTUnwrap(macroProducts.first) - XCTAssertEqual(macroProduct.buildParameters.triple, toolsTriple) - - // FIXME: check for *toolsTriple* - let mmioTarget = try XCTUnwrap(plan.targets.first { try $0.swiftTarget().target.name == "MMIO" }?.swiftTarget()) - let compileArguments = try mmioTarget.emitCommandLine() - XCTAssertMatch( - compileArguments, - [ - "-I", .equal(mmioTarget.moduleOutputPath.parentDirectory.pathString), - .anySequence, - "-Xfrontend", "-load-plugin-executable", - "-Xfrontend", .contains(toolsTriple.tripleString) - ] - ) - } -} diff --git a/Tests/BuildTests/LLBuildManifestBuilderTests.swift b/Tests/BuildTests/LLBuildManifestBuilderTests.swift index 0450fcd8f39..d577e577c7a 100644 --- a/Tests/BuildTests/LLBuildManifestBuilderTests.swift +++ b/Tests/BuildTests/LLBuildManifestBuilderTests.swift @@ -50,7 +50,7 @@ final class LLBuildManifestBuilderTests: XCTestCase { configuration: .release )) var plan = try BuildPlan( - destinationBuildParameters: buildParameters, + productsBuildParameters: buildParameters, toolsBuildParameters: buildParameters, graph: graph, fileSystem: fs, @@ -67,8 +67,8 @@ final class LLBuildManifestBuilderTests: XCTestCase { ) try llbuild.createProductCommand(buildProduct) - var basicReleaseCommandNames = [ - AbsolutePath("/path/to/build/\(buildParameters.triple)/release/exe.product/Objects.LinkFileList").pathString, + let basicReleaseCommandNames = [ + AbsolutePath("/path/to/build/release/exe.product/Objects.LinkFileList").pathString, "", "C.exe-release.exe", ] @@ -85,7 +85,7 @@ final class LLBuildManifestBuilderTests: XCTestCase { configuration: .debug )) plan = try BuildPlan( - destinationBuildParameters: buildParameters, + productsBuildParameters: buildParameters, toolsBuildParameters: buildParameters, graph: graph, fileSystem: fs, @@ -95,12 +95,12 @@ final class LLBuildManifestBuilderTests: XCTestCase { result = try BuildPlanResult(plan: plan) buildProduct = try result.buildProduct(for: "exe") - llbuild = LLBuildManifestBuilder(plan, fileSystem: fs, observabilityScope: observability.topScope) + llbuild = LLBuildManifestBuilder(plan, fileSystem: localFileSystem, observabilityScope: observability.topScope) try llbuild.createProductCommand(buildProduct) let entitlementsCommandName = "C.exe-debug.exe-entitlements" - var basicDebugCommandNames = [ - AbsolutePath("/path/to/build/\(buildParameters.triple)/debug/exe.product/Objects.LinkFileList").pathString, + let basicDebugCommandNames = [ + AbsolutePath("/path/to/build/debug/exe.product/Objects.LinkFileList").pathString, "", "C.exe-debug.exe", ] @@ -108,7 +108,7 @@ final class LLBuildManifestBuilderTests: XCTestCase { XCTAssertEqual( llbuild.manifest.commands.map(\.key).sorted(), (basicDebugCommandNames + [ - AbsolutePath("/path/to/build/\(buildParameters.triple)/debug/exe-entitlement.plist").pathString, + AbsolutePath("/path/to/build/debug/exe-entitlement.plist").pathString, entitlementsCommandName, ]).sorted() ) @@ -121,8 +121,8 @@ final class LLBuildManifestBuilderTests: XCTestCase { XCTAssertEqual( entitlementsCommand.inputs, [ - .file("/path/to/build/\(buildParameters.triple)/debug/exe", isMutated: true), - .file("/path/to/build/\(buildParameters.triple)/debug/exe-entitlement.plist"), + .file("/path/to/build/debug/exe", isMutated: true), + .file("/path/to/build/debug/exe-entitlement.plist"), ] ) XCTAssertEqual( @@ -139,7 +139,7 @@ final class LLBuildManifestBuilderTests: XCTestCase { configuration: .release )) plan = try BuildPlan( - destinationBuildParameters: buildParameters, + productsBuildParameters: buildParameters, toolsBuildParameters: buildParameters, graph: graph, fileSystem: fs, @@ -152,12 +152,6 @@ final class LLBuildManifestBuilderTests: XCTestCase { llbuild = LLBuildManifestBuilder(plan, fileSystem: localFileSystem, observabilityScope: observability.topScope) try llbuild.createProductCommand(buildProduct) - basicReleaseCommandNames = [ - AbsolutePath("/path/to/build/\(buildParameters.triple)/release/exe.product/Objects.LinkFileList").pathString, - "", - "C.exe-release.exe", - ] - XCTAssertEqual( llbuild.manifest.commands.map(\.key).sorted(), basicReleaseCommandNames.sorted() @@ -170,7 +164,7 @@ final class LLBuildManifestBuilderTests: XCTestCase { configuration: .debug )) plan = try BuildPlan( - destinationBuildParameters: buildParameters, + productsBuildParameters: buildParameters, toolsBuildParameters: buildParameters, graph: graph, fileSystem: fs, @@ -180,38 +174,12 @@ final class LLBuildManifestBuilderTests: XCTestCase { result = try BuildPlanResult(plan: plan) buildProduct = try result.buildProduct(for: "exe") - llbuild = LLBuildManifestBuilder(plan, fileSystem: fs, observabilityScope: observability.topScope) + llbuild = LLBuildManifestBuilder(plan, fileSystem: localFileSystem, observabilityScope: observability.topScope) try llbuild.createProductCommand(buildProduct) - basicDebugCommandNames = [ - AbsolutePath("/path/to/build/\(buildParameters.triple)/debug/exe.product/Objects.LinkFileList").pathString, - "", - "C.exe-debug.exe", - ] - XCTAssertEqual( llbuild.manifest.commands.map(\.key).sorted(), basicDebugCommandNames.sorted() ) } - - /// Verifies that two targets with the same name but different triples don't share same build manifest keys. - func testToolsBuildTriple() throws { - let (graph, fs, scope) = try macrosPackageGraph() - let productsTriple = Triple.x86_64MacOS - let toolsTriple = Triple.arm64Linux - - let plan = try BuildPlan( - destinationBuildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true, triple: productsTriple), - toolsBuildParameters: mockBuildParameters(triple: toolsTriple), - graph: graph, - fileSystem: fs, - observabilityScope: scope - ) - - let builder = LLBuildManifestBuilder(plan, fileSystem: fs, observabilityScope: scope) - let manifest = try builder.generateManifest(at: "/manifest") - - XCTAssertNotNil(manifest.commands["C.SwiftSyntax-debug-tool.module"]) - } } diff --git a/Tests/BuildTests/ModuleAliasingBuildTests.swift b/Tests/BuildTests/ModuleAliasingBuildTests.swift index 6101be96fb1..090dedb431a 100644 --- a/Tests/BuildTests/ModuleAliasingBuildTests.swift +++ b/Tests/BuildTests/ModuleAliasingBuildTests.swift @@ -694,9 +694,8 @@ final class ModuleAliasingBuildTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) - let buildParameters = mockBuildParameters(shouldLinkStaticSwiftStdlib: true) let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: buildParameters, + buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -725,33 +724,33 @@ final class ModuleAliasingBuildTests: XCTestCase { XCTAssertMatch( fooLoggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/\(buildParameters.triple)/debug/FooLogging.build/FooLogging-Swift.h", .anySequence] + "/path/to/build/debug/FooLogging.build/FooLogging-Swift.h", .anySequence] ) XCTAssertMatch( barLoggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/\(buildParameters.triple)/debug/BarLogging.build/BarLogging-Swift.h", .anySequence] + "/path/to/build/debug/BarLogging.build/BarLogging-Swift.h", .anySequence] ) XCTAssertMatch( loggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/\(buildParameters.triple)/debug/Logging.build/Logging-Swift.h", .anySequence] + "/path/to/build/debug/Logging.build/Logging-Swift.h", .anySequence] ) #else XCTAssertNoMatch( fooLoggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/\(buildParameters.triple)/debug/FooLogging.build/FooLogging-Swift.h", .anySequence] + "/path/to/build/debug/FooLogging.build/FooLogging-Swift.h", .anySequence] ) XCTAssertNoMatch( barLoggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/\(buildParameters.triple)/debug/BarLogging.build/BarLogging-Swift.h", .anySequence] + "/path/to/build/debug/BarLogging.build/BarLogging-Swift.h", .anySequence] ) XCTAssertNoMatch( loggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/\(buildParameters.triple)/debug/Logging.build/Logging-Swift.h", .anySequence] + "/path/to/build/debug/Logging.build/Logging-Swift.h", .anySequence] ) #endif } @@ -813,9 +812,8 @@ final class ModuleAliasingBuildTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let buildParameters = mockBuildParameters(shouldLinkStaticSwiftStdlib: true) let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: buildParameters, + buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -844,23 +842,23 @@ final class ModuleAliasingBuildTests: XCTestCase { XCTAssertMatch( otherLoggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/\(buildParameters.triple)/debug/OtherLogging.build/OtherLogging-Swift.h", .anySequence] + "/path/to/build/debug/OtherLogging.build/OtherLogging-Swift.h", .anySequence] ) XCTAssertMatch( loggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/\(buildParameters.triple)/debug/Logging.build/Logging-Swift.h", .anySequence] + "/path/to/build/debug/Logging.build/Logging-Swift.h", .anySequence] ) #else XCTAssertNoMatch( otherLoggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/\(buildParameters.triple)/debug/OtherLogging.build/OtherLogging-Swift.h", .anySequence] + "/path/to/build/debug/OtherLogging.build/OtherLogging-Swift.h", .anySequence] ) XCTAssertNoMatch( loggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/\(buildParameters.triple)/debug/Logging.build/Logging-Swift.h", .anySequence] + "/path/to/build/debug/Logging.build/Logging-Swift.h", .anySequence] ) #endif } diff --git a/Tests/BuildTests/PluginsBuildPlanTests.swift b/Tests/BuildTests/PluginsBuildPlanTests.swift index 7b2a1a78378..5ef99e3fc59 100644 --- a/Tests/BuildTests/PluginsBuildPlanTests.swift +++ b/Tests/BuildTests/PluginsBuildPlanTests.swift @@ -19,7 +19,7 @@ import PackageModel final class PluginsBuildPlanTests: XCTestCase { func testBuildToolsDatabasePath() throws { try fixture(name: "Miscellaneous/Plugins/MySourceGenPlugin") { fixturePath in - let (stdout, _) = try executeSwiftBuild(fixturePath) + let (stdout, stderr) = try executeSwiftBuild(fixturePath) XCTAssertMatch(stdout, .contains("Build complete!")) XCTAssertTrue(localFileSystem.exists(fixturePath.appending(RelativePath(".build/plugins/tools/build.db")))) } @@ -48,16 +48,8 @@ final class PluginsBuildPlanTests: XCTestCase { let (stdout, stderr) = try executeSwiftPackage(fixturePath, extraArgs: ["-v", "build-plugin-dependency"]) XCTAssertMatch(stdout, .contains("Hello from dependencies-stub")) XCTAssertMatch(stderr, .contains("Build of product 'plugintool' complete!")) - XCTAssertTrue( - localFileSystem.exists( - fixturePath.appending(RelativePath(".build/\(hostTriple)/debug/plugintool-tool")) - ) - ) - XCTAssertTrue( - localFileSystem.exists( - fixturePath.appending(RelativePath(".build/\(hostTriple)/debug/placeholder")) - ) - ) + XCTAssertTrue(localFileSystem.exists(fixturePath.appending(RelativePath(".build/\(hostTriple)/debug/plugintool")))) + XCTAssertTrue(localFileSystem.exists(fixturePath.appending(RelativePath(".build/\(hostTriple)/debug/placeholder")))) } // When cross compiling the final product, plugin dependencies should still be built for the host @@ -65,16 +57,8 @@ final class PluginsBuildPlanTests: XCTestCase { let (stdout, stderr) = try executeSwiftPackage(fixturePath, extraArgs: ["--triple", targetTriple, "-v", "build-plugin-dependency"]) XCTAssertMatch(stdout, .contains("Hello from dependencies-stub")) XCTAssertMatch(stderr, .contains("Build of product 'plugintool' complete!")) - XCTAssertTrue( - localFileSystem.exists( - fixturePath.appending(RelativePath(".build/\(hostTriple)/debug/plugintool-tool")) - ) - ) - XCTAssertTrue( - localFileSystem.exists( - fixturePath.appending(RelativePath(".build/\(targetTriple)/debug/placeholder")) - ) - ) + XCTAssertTrue(localFileSystem.exists(fixturePath.appending(RelativePath(".build/\(hostTriple)/debug/plugintool")))) + XCTAssertTrue(localFileSystem.exists(fixturePath.appending(RelativePath(".build/\(targetTriple)/debug/placeholder")))) } } } diff --git a/Tests/CommandsTests/PackageToolTests.swift b/Tests/CommandsTests/PackageToolTests.swift index dbc6b492dad..4d91794c8bd 100644 --- a/Tests/CommandsTests/PackageToolTests.swift +++ b/Tests/CommandsTests/PackageToolTests.swift @@ -1714,15 +1714,9 @@ final class PackageToolTests: CommandsTestCase { """ ) let hostTriple = try UserToolchain(swiftSDK: .hostSwiftSDK()).targetTriple - let hostTripleString = if hostTriple.isDarwin() { - hostTriple.tripleString(forPlatformVersion: "") - } else { - hostTriple.tripleString - } - - try localFileSystem.writeFileContents( - packageDir.appending(components: "Binaries", "LocalBinaryTool.artifactbundle", "info.json"), - string: """ + let hostTripleString = hostTriple.isDarwin() ? hostTriple.tripleString(forPlatformVersion: "") : hostTriple.tripleString + try localFileSystem.writeFileContents(packageDir.appending(components: "Binaries", "LocalBinaryTool.artifactbundle", "info.json"), string: + """ { "schemaVersion": "1.0", "artifacts": { "LocalBinaryTool": { @@ -1738,13 +1732,11 @@ final class PackageToolTests: CommandsTestCase { } """ ) - try localFileSystem.writeFileContents( - packageDir.appending(components: "Sources", "LocalBuiltTool", "main.swift"), - string: #"print("Hello")"# + try localFileSystem.writeFileContents(packageDir.appending(components: "Sources", "LocalBuiltTool", "main.swift"), string: + #"print("Hello")"# ) - try localFileSystem.writeFileContents( - packageDir.appending(components: "Plugins", "MyPlugin", "plugin.swift"), - string: """ + try localFileSystem.writeFileContents(packageDir.appending(components: "Plugins", "MyPlugin", "plugin.swift"), string: + """ import PackagePlugin import Foundation @main @@ -1800,9 +1792,8 @@ final class PackageToolTests: CommandsTestCase { ) // Create the sample vendored dependency package. - try localFileSystem.writeFileContents( - packageDir.appending(components: "VendoredDependencies", "HelperPackage", "Package.swift"), - string: """ + try localFileSystem.writeFileContents(packageDir.appending(components: "VendoredDependencies", "HelperPackage", "Package.swift"), string: + """ // swift-tools-version: 5.5 import PackageDescription let package = Package( @@ -1828,25 +1819,9 @@ final class PackageToolTests: CommandsTestCase { ) """ ) - try localFileSystem.writeFileContents( - packageDir.appending( - components: "VendoredDependencies", - "HelperPackage", - "Sources", - "HelperLibrary", - "library.swift" - ), - string: "public func Bar() { }" + try localFileSystem.writeFileContents(packageDir.appending(components: "VendoredDependencies", "HelperPackage", "Sources", "HelperLibrary", "library.swift"), string: "public func Bar() { }" ) - try localFileSystem.writeFileContents( - packageDir.appending( - components: "VendoredDependencies", - "HelperPackage", - "Sources", - "RemoteBuiltTool", - "main.swift" - ), - string: #"print("Hello")"# + try localFileSystem.writeFileContents(packageDir.appending(components: "VendoredDependencies", "HelperPackage", "Sources", "RemoteBuiltTool", "main.swift"), string: #"print("Hello")"# ) // Check that we can invoke the plugin with the "plugin" subcommand. diff --git a/Tests/CommandsTests/SwiftToolTests.swift b/Tests/CommandsTests/SwiftToolTests.swift index 44e8ea2c75a..cc5c291de0e 100644 --- a/Tests/CommandsTests/SwiftToolTests.swift +++ b/Tests/CommandsTests/SwiftToolTests.swift @@ -255,7 +255,7 @@ final class SwiftToolTests: CommandsTestCase { let explicitDwarfOptions = try GlobalOptions.parse(["--triple", "x86_64-unknown-windows-msvc", "-debug-info-format", "dwarf"]) let explicitDwarf = try SwiftTool.createSwiftToolForTest(options: explicitDwarfOptions) plan = try BuildPlan( - destinationBuildParameters: explicitDwarf.productsBuildParameters, + productsBuildParameters: explicitDwarf.productsBuildParameters, toolsBuildParameters: explicitDwarf.toolsBuildParameters, graph: graph, fileSystem: fs, @@ -270,7 +270,7 @@ final class SwiftToolTests: CommandsTestCase { let explicitCodeView = try SwiftTool.createSwiftToolForTest(options: explicitCodeViewOptions) plan = try BuildPlan( - destinationBuildParameters: explicitCodeView.productsBuildParameters, + productsBuildParameters: explicitCodeView.productsBuildParameters, toolsBuildParameters: explicitCodeView.productsBuildParameters, graph: graph, fileSystem: fs, @@ -293,7 +293,7 @@ final class SwiftToolTests: CommandsTestCase { let implicitDwarfOptions = try GlobalOptions.parse(["--triple", "x86_64-unknown-windows-msvc"]) let implicitDwarf = try SwiftTool.createSwiftToolForTest(options: implicitDwarfOptions) plan = try BuildPlan( - destinationBuildParameters: implicitDwarf.productsBuildParameters, + productsBuildParameters: implicitDwarf.productsBuildParameters, toolsBuildParameters: implicitDwarf.toolsBuildParameters, graph: graph, fileSystem: fs, @@ -306,7 +306,7 @@ final class SwiftToolTests: CommandsTestCase { let explicitNoDebugInfoOptions = try GlobalOptions.parse(["--triple", "x86_64-unknown-windows-msvc", "-debug-info-format", "none"]) let explicitNoDebugInfo = try SwiftTool.createSwiftToolForTest(options: explicitNoDebugInfoOptions) plan = try BuildPlan( - destinationBuildParameters: explicitNoDebugInfo.productsBuildParameters, + productsBuildParameters: explicitNoDebugInfo.productsBuildParameters, toolsBuildParameters: explicitNoDebugInfo.toolsBuildParameters, graph: graph, fileSystem: fs, diff --git a/Tests/PackageGraphTests/CrossCompilationPackageGraphTests.swift b/Tests/PackageGraphTests/CrossCompilationPackageGraphTests.swift deleted file mode 100644 index 79076d69adc..00000000000 --- a/Tests/PackageGraphTests/CrossCompilationPackageGraphTests.swift +++ /dev/null @@ -1,61 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift open source project -// -// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See http://swift.org/LICENSE.txt for license information -// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -@testable -import SPMTestSupport - -@testable -import PackageGraph - -import XCTest - -final class CrossCompilationPackageGraphTests: XCTestCase { - func testMacros() throws { - let graph = try macrosPackageGraph().graph - PackageGraphTester(graph) { result in - result.check(packages: "swift-firmware", "swift-mmio", "swift-syntax") - // "SwiftSyntax" is included for both host and target triples and is not pruned on this level - result.check(targets: "Core", "HAL", "MMIO", "MMIOMacros", "SwiftSyntax", "SwiftSyntax") - result.check(testModules: "CoreTests", "HALTests") - result.checkTarget("Core") { result in - result.check(buildTriple: .destination) - result.check(dependencies: "HAL") - } - result.checkTarget("HAL") { result in - result.check(buildTriple: .destination) - result.check(dependencies: "MMIO") - } - result.checkTarget("MMIO") { result in - result.check(buildTriple: .destination) - result.check(dependencies: "MMIOMacros") - } - result.checkTarget("MMIOMacros") { result in - result.check(buildTriple: .tools) - result.checkDependency("SwiftSyntax") { result in - result.checkProduct { result in - result.check(buildTriple: .tools) - result.checkTarget("SwiftSyntax") { result in - result.check(buildTriple: .tools) - } - } - } - } - - result.checkTargets("SwiftSyntax") { results in - XCTAssertEqual(results.count, 2) - - XCTAssertEqual(results.filter({ $0.target.buildTriple == .tools }).count, 1) - XCTAssertEqual(results.filter({ $0.target.buildTriple == .destination }).count, 1) - } - } - } -} diff --git a/Tests/PackageGraphTests/PackageGraphTests.swift b/Tests/PackageGraphTests/PackageGraphTests.swift index ab86f67d6a8..27af5fee6c4 100644 --- a/Tests/PackageGraphTests/PackageGraphTests.swift +++ b/Tests/PackageGraphTests/PackageGraphTests.swift @@ -19,7 +19,8 @@ import XCTest import struct TSCBasic.ByteString import class TSCBasic.InMemoryFileSystem -final class PackageGraphTests: XCTestCase { +class PackageGraphTests: XCTestCase { + func testBasic() throws { let fs = InMemoryFileSystem(emptyFiles: "/Foo/Sources/Foo/source.swift", diff --git a/Tests/SPMBuildCoreTests/PluginInvocationTests.swift b/Tests/SPMBuildCoreTests/PluginInvocationTests.swift index 3d5da4c1255..d06d5bb133d 100644 --- a/Tests/SPMBuildCoreTests/PluginInvocationTests.swift +++ b/Tests/SPMBuildCoreTests/PluginInvocationTests.swift @@ -74,8 +74,7 @@ class PluginInvocationTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) PackageGraphTester(graph) { graph in graph.check(packages: "Foo") - // "FooTool" duplicated as it's present for both build tools and end products triples. - graph.check(targets: "Foo", "FooPlugin", "FooTool", "FooTool") + graph.check(targets: "Foo", "FooPlugin", "FooTool") graph.checkTarget("Foo") { target in target.check(dependencies: "FooPlugin") } @@ -189,13 +188,13 @@ class PluginInvocationTests: XCTestCase { // Construct a canned input and run plugins using our MockPluginScriptRunner(). let outputDir = AbsolutePath("/Foo/.build") + let builtToolsDir = AbsolutePath("/path/to/build/debug") let pluginRunner = MockPluginScriptRunner() - let buildParameters = mockBuildParameters( - environment: BuildEnvironment(platform: .macOS, configuration: .debug) - ) let results = try graph.invokeBuildToolPlugins( outputDir: outputDir, - buildParameters: buildParameters, + buildParameters: mockBuildParameters( + environment: BuildEnvironment(platform: .macOS, configuration: .debug) + ), additionalFileRules: [], toolSearchDirectories: [UserToolchain.default.swiftCompilerPath.parentDirectory], pkgConfigDirectories: [], @@ -203,7 +202,6 @@ class PluginInvocationTests: XCTestCase { observabilityScope: observability.topScope, fileSystem: fileSystem ) - let builtToolsDir = AbsolutePath("/path/to/build/\(buildParameters.triple)/debug") // Check the canned output to make sure nothing was lost in transport. XCTAssertNoDiagnostics(observability.diagnostics) diff --git a/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift b/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift index 7541f543566..18cbcd13d14 100644 --- a/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift +++ b/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift @@ -42,38 +42,17 @@ class SourceKitLSPAPITests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let buildParameters = mockBuildParameters(shouldLinkStaticSwiftStdlib: true) let plan = try BuildPlan( - destinationBuildParameters: buildParameters, - toolsBuildParameters: buildParameters, + productsBuildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + toolsBuildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), graph: graph, fileSystem: fs, observabilityScope: observability.topScope ) let description = BuildDescription(buildPlan: plan) - try description.checkArguments( - for: "exe", - graph: graph, - partialArguments: [ - "/fake/path/to/swiftc", - "-module-name", "exe", - "-emit-dependencies", - "-emit-module", - "-emit-module-path", "/path/to/build/\(buildParameters.triple)/debug/exe.build/exe.swiftmodule" - ] - ) - try description.checkArguments( - for: "lib", - graph: graph, - partialArguments: [ - "/fake/path/to/swiftc", - "-module-name", "lib", - "-emit-dependencies", - "-emit-module", - "-emit-module-path", "/path/to/build/\(buildParameters.triple)/debug/Modules/lib.swiftmodule" - ] - ) + try description.checkArguments(for: "exe", graph: graph, partialArguments: ["/fake/path/to/swiftc", "-module-name", "exe", "-emit-dependencies", "-emit-module", "-emit-module-path", "/path/to/build/debug/exe.build/exe.swiftmodule"]) + try description.checkArguments(for: "lib", graph: graph, partialArguments: ["/fake/path/to/swiftc", "-module-name", "lib", "-emit-dependencies", "-emit-module", "-emit-module-path", "/path/to/build/debug/Modules/lib.swiftmodule"]) } } From c82304ce1c15cb981e0d60cc1486bd5c271548bc Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Wed, 21 Feb 2024 10:53:05 +0000 Subject: [PATCH 014/159] Build: pass through `Embedded` flag to link jobs (#7304) ### Motivation: Without passing this flag, Swift Driver links such products as if they're not built for embedded platforms at all, passing incorrect flags to the linker. This is not reproducible when using plain `swiftc`, as that combines both compile and link jobs by default, but SwiftPM prefers to run those separately, which requires this special handling. ### Modifications: Directly checking in `ProductBuildDescription/linkArguments` whether all of the product's targets are built in the embedded mode. If so, we're passing the embedded mode flag to Swift Driver. Unfortunately, `BuildSettings.AssignmentTable` is formed too early in the build process right in `PackageModel`, while we still need to run checks specific build settings quite late in the build stage. Because of that, there's no clean way to check if `Embedded` flag is passed other than directly rendering `BuildSettings.Scope` to a string and running a substring check on that. In the future we should move `BuildSettings` out of `PackageModel`, ensuring that SwiftPM clients don't rely on this behavior anymore. ### Result: Products that have targets using Embedded Swift can be built with SwiftPM, assuming that Swift Driver handles other linker flags correctly. --- .../ProductBuildDescription.swift | 28 +++++-- Sources/PackageLoading/PackageBuilder.swift | 48 +++++++----- .../PackageModel/Target/BinaryTarget.swift | 1 + Sources/PackageModel/Target/ClangTarget.swift | 2 + .../PackageModel/Target/PluginTarget.swift | 1 + Sources/PackageModel/Target/SwiftTarget.swift | 4 + .../Target/SystemLibraryTarget.swift | 1 + Sources/PackageModel/Target/Target.swift | 26 ++++++- .../ProductBuildDescriptionTests.swift | 75 +++++++++++++++++++ 9 files changed, 158 insertions(+), 28 deletions(-) create mode 100644 Tests/BuildTests/ProductBuildDescriptionTests.swift diff --git a/Sources/Build/BuildDescription/ProductBuildDescription.swift b/Sources/Build/BuildDescription/ProductBuildDescription.swift index bb96cd383a2..2eec24fa713 100644 --- a/Sources/Build/BuildDescription/ProductBuildDescription.swift +++ b/Sources/Build/BuildDescription/ProductBuildDescription.swift @@ -12,7 +12,10 @@ import Basics import PackageGraph + +@_spi(SwiftPMInternal) import PackageModel + import OrderedCollections import SPMBuildCore @@ -198,7 +201,7 @@ public final class ProductBuildDescription: SPMBuildCore.ProductBuildDescription // No arguments for static libraries. return [] case .test: - // Test products are bundle when using objectiveC, executable when using test entry point. + // Test products are bundle when using Objective-C, executable when using test entry point. switch self.buildParameters.testingParameters.testProductStyle { case .loadableBundle: args += ["-Xlinker", "-bundle"] @@ -271,8 +274,21 @@ public final class ProductBuildDescription: SPMBuildCore.ProductBuildDescription } args += ["@\(self.linkFileListPath.pathString)"] - // Embed the swift stdlib library path inside tests and executables on Darwin. if containsSwiftTargets { + // Pass experimental features to link jobs in addition to compile jobs. Preserve ordering while eliminating + // duplicates with `OrderedSet`. + var experimentalFeatures = OrderedSet() + for target in self.product.targets { + let swiftSettings = target.underlying.buildSettingsDescription.filter { $0.tool == .swift } + for case let .enableExperimentalFeature(feature) in swiftSettings.map(\.kind) { + experimentalFeatures.append(feature) + } + } + for feature in experimentalFeatures { + args += ["-enable-experimental-feature", feature] + } + + // Embed the swift stdlib library path inside tests and executables on Darwin. let useStdlibRpath: Bool switch self.product.type { case .library(let type): @@ -297,11 +313,9 @@ public final class ProductBuildDescription: SPMBuildCore.ProductBuildDescription args += ["-Xlinker", "-rpath", "-Xlinker", backDeployedStdlib.pathString] } } - } - - // Don't link runtime compatibility patch libraries if there are no - // Swift sources in the target. - if !containsSwiftTargets { + } else { + // Don't link runtime compatibility patch libraries if there are no + // Swift sources in the target. args += ["-runtime-compatibility-version", "none"] } diff --git a/Sources/PackageLoading/PackageBuilder.swift b/Sources/PackageLoading/PackageBuilder.swift index 9379a7d099d..a1c13034e67 100644 --- a/Sources/PackageLoading/PackageBuilder.swift +++ b/Sources/PackageLoading/PackageBuilder.swift @@ -877,7 +877,11 @@ public final class PackageBuilder { } // Create the build setting assignment table for this target. - let buildSettings = try self.buildSettings(for: manifestTarget, targetRoot: potentialModule.path, cxxLanguageStandard: self.manifest.cxxLanguageStandard) + let buildSettings = try self.buildSettings( + for: manifestTarget, + targetRoot: potentialModule.path, + cxxLanguageStandard: self.manifest.cxxLanguageStandard + ) // Compute the path to public headers directory. let publicHeaderComponent = manifestTarget.publicHeadersPath ?? ClangTarget.defaultPublicHeadersComponent @@ -969,6 +973,7 @@ public final class PackageBuilder { packageAccess: potentialModule.packageAccess, swiftVersion: try self.swiftVersion(), buildSettings: buildSettings, + buildSettingsDescription: manifestTarget.settings, usesUnsafeFlags: manifestTarget.usesUnsafeFlags ) } else { @@ -1011,14 +1016,18 @@ public final class PackageBuilder { ignored: ignored, dependencies: dependencies, buildSettings: buildSettings, + buildSettingsDescription: manifestTarget.settings, usesUnsafeFlags: manifestTarget.usesUnsafeFlags ) } } /// Creates build setting assignment table for the given target. - func buildSettings(for target: TargetDescription?, targetRoot: AbsolutePath, cxxLanguageStandard: String? = nil) throws -> BuildSettings - .AssignmentTable + func buildSettings( + for target: TargetDescription?, + targetRoot: AbsolutePath, + cxxLanguageStandard: String? = nil + ) throws -> BuildSettings.AssignmentTable { var table = BuildSettings.AssignmentTable() guard let target else { return table } @@ -1653,23 +1662,21 @@ extension PackageBuilder { let sources = Sources(paths: [sourceFile], root: sourceFile.parentDirectory) let buildSettings: BuildSettings.AssignmentTable - do { - let targetDescription = try TargetDescription( - name: name, - dependencies: dependencies - .map { - TargetDescription.Dependency.target(name: $0.name) - }, - path: sourceFile.parentDirectory.pathString, - sources: [sourceFile.pathString], - type: .executable, - packageAccess: false - ) - buildSettings = try self.buildSettings( - for: targetDescription, - targetRoot: sourceFile.parentDirectory - ) - } + let targetDescription = try TargetDescription( + name: name, + dependencies: dependencies + .map { + TargetDescription.Dependency.target(name: $0.name) + }, + path: sourceFile.parentDirectory.pathString, + sources: [sourceFile.pathString], + type: .executable, + packageAccess: false + ) + buildSettings = try self.buildSettings( + for: targetDescription, + targetRoot: sourceFile.parentDirectory + ) return SwiftTarget( name: name, @@ -1680,6 +1687,7 @@ extension PackageBuilder { packageAccess: false, swiftVersion: try swiftVersion(), buildSettings: buildSettings, + buildSettingsDescription: targetDescription.settings, usesUnsafeFlags: false ) } diff --git a/Sources/PackageModel/Target/BinaryTarget.swift b/Sources/PackageModel/Target/BinaryTarget.swift index cf61289be2b..8d42d7b7b78 100644 --- a/Sources/PackageModel/Target/BinaryTarget.swift +++ b/Sources/PackageModel/Target/BinaryTarget.swift @@ -41,6 +41,7 @@ public final class BinaryTarget: Target { dependencies: [], packageAccess: false, buildSettings: .init(), + buildSettingsDescription: [], pluginUsages: [], usesUnsafeFlags: false ) diff --git a/Sources/PackageModel/Target/ClangTarget.swift b/Sources/PackageModel/Target/ClangTarget.swift index d38f559a40d..e47b0baf516 100644 --- a/Sources/PackageModel/Target/ClangTarget.swift +++ b/Sources/PackageModel/Target/ClangTarget.swift @@ -53,6 +53,7 @@ public final class ClangTarget: Target { others: [AbsolutePath] = [], dependencies: [Target.Dependency] = [], buildSettings: BuildSettings.AssignmentTable = .init(), + buildSettingsDescription: [TargetBuildSettingDescription.Setting] = [], usesUnsafeFlags: Bool ) throws { guard includeDir.isDescendantOfOrEqual(to: sources.root) else { @@ -76,6 +77,7 @@ public final class ClangTarget: Target { dependencies: dependencies, packageAccess: false, buildSettings: buildSettings, + buildSettingsDescription: buildSettingsDescription, pluginUsages: [], usesUnsafeFlags: usesUnsafeFlags ) diff --git a/Sources/PackageModel/Target/PluginTarget.swift b/Sources/PackageModel/Target/PluginTarget.swift index df195bcc799..84d04d0fee5 100644 --- a/Sources/PackageModel/Target/PluginTarget.swift +++ b/Sources/PackageModel/Target/PluginTarget.swift @@ -36,6 +36,7 @@ public final class PluginTarget: Target { dependencies: dependencies, packageAccess: packageAccess, buildSettings: .init(), + buildSettingsDescription: [], pluginUsages: [], usesUnsafeFlags: false ) diff --git a/Sources/PackageModel/Target/SwiftTarget.swift b/Sources/PackageModel/Target/SwiftTarget.swift index f23f79db6fe..13e16d5b30e 100644 --- a/Sources/PackageModel/Target/SwiftTarget.swift +++ b/Sources/PackageModel/Target/SwiftTarget.swift @@ -33,6 +33,7 @@ public final class SwiftTarget: Target { dependencies: dependencies, packageAccess: packageAccess, buildSettings: .init(), + buildSettingsDescription: [], pluginUsages: [], usesUnsafeFlags: false ) @@ -54,6 +55,7 @@ public final class SwiftTarget: Target { packageAccess: Bool, swiftVersion: SwiftLanguageVersion, buildSettings: BuildSettings.AssignmentTable = .init(), + buildSettingsDescription: [TargetBuildSettingDescription.Setting] = [], pluginUsages: [PluginUsage] = [], usesUnsafeFlags: Bool ) { @@ -70,6 +72,7 @@ public final class SwiftTarget: Target { dependencies: dependencies, packageAccess: packageAccess, buildSettings: buildSettings, + buildSettingsDescription: buildSettingsDescription, pluginUsages: pluginUsages, usesUnsafeFlags: usesUnsafeFlags ) @@ -100,6 +103,7 @@ public final class SwiftTarget: Target { dependencies: dependencies, packageAccess: packageAccess, buildSettings: .init(), + buildSettingsDescription: [], pluginUsages: [], usesUnsafeFlags: false ) diff --git a/Sources/PackageModel/Target/SystemLibraryTarget.swift b/Sources/PackageModel/Target/SystemLibraryTarget.swift index beb99b670e5..a33af62afc5 100644 --- a/Sources/PackageModel/Target/SystemLibraryTarget.swift +++ b/Sources/PackageModel/Target/SystemLibraryTarget.swift @@ -43,6 +43,7 @@ public final class SystemLibraryTarget: Target { dependencies: [], packageAccess: false, buildSettings: .init(), + buildSettingsDescription: [], pluginUsages: [], usesUnsafeFlags: false ) diff --git a/Sources/PackageModel/Target/Target.swift b/Sources/PackageModel/Target/Target.swift index 541e37794de..4754fe185e5 100644 --- a/Sources/PackageModel/Target/Target.swift +++ b/Sources/PackageModel/Target/Target.swift @@ -233,6 +233,9 @@ public class Target: PolymorphicCodableProtocol { /// The build settings assignments of this target. public let buildSettings: BuildSettings.AssignmentTable + @_spi(SwiftPMInternal) + public let buildSettingsDescription: [TargetBuildSettingDescription.Setting] + /// The usages of package plugins by this target. public let pluginUsages: [PluginUsage] @@ -251,6 +254,7 @@ public class Target: PolymorphicCodableProtocol { dependencies: [Target.Dependency], packageAccess: Bool, buildSettings: BuildSettings.AssignmentTable, + buildSettingsDescription: [TargetBuildSettingDescription.Setting], pluginUsages: [PluginUsage], usesUnsafeFlags: Bool ) { @@ -266,12 +270,27 @@ public class Target: PolymorphicCodableProtocol { self.c99name = self.name.spm_mangledToC99ExtendedIdentifier() self.packageAccess = packageAccess self.buildSettings = buildSettings + self.buildSettingsDescription = buildSettingsDescription self.pluginUsages = pluginUsages self.usesUnsafeFlags = usesUnsafeFlags } private enum CodingKeys: String, CodingKey { - case name, potentialBundleName, defaultLocalization, platforms, type, path, sources, resources, ignored, others, packageAccess, buildSettings, pluginUsages, usesUnsafeFlags + case name + case potentialBundleName + case defaultLocalization + case platforms + case type + case path + case sources + case resources + case ignored + case others + case packageAccess + case buildSettings + case buildSettingsDescription + case pluginUsages + case usesUnsafeFlags } public func encode(to encoder: Encoder) throws { @@ -289,6 +308,7 @@ public class Target: PolymorphicCodableProtocol { try container.encode(others, forKey: .others) try container.encode(packageAccess, forKey: .packageAccess) try container.encode(buildSettings, forKey: .buildSettings) + try container.encode(buildSettingsDescription, forKey: .buildSettingsDescription) // FIXME: pluginUsages property is skipped on purpose as it points to // the actual target dependency object. try container.encode(usesUnsafeFlags, forKey: .usesUnsafeFlags) @@ -310,6 +330,10 @@ public class Target: PolymorphicCodableProtocol { self.c99name = self.name.spm_mangledToC99ExtendedIdentifier() self.packageAccess = try container.decode(Bool.self, forKey: .packageAccess) self.buildSettings = try container.decode(BuildSettings.AssignmentTable.self, forKey: .buildSettings) + self.buildSettingsDescription = try container.decode( + [TargetBuildSettingDescription.Setting].self, + forKey: .buildSettingsDescription + ) // FIXME: pluginUsages property is skipped on purpose as it points to // the actual target dependency object. self.pluginUsages = [] diff --git a/Tests/BuildTests/ProductBuildDescriptionTests.swift b/Tests/BuildTests/ProductBuildDescriptionTests.swift new file mode 100644 index 00000000000..40d1c906c2c --- /dev/null +++ b/Tests/BuildTests/ProductBuildDescriptionTests.swift @@ -0,0 +1,75 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2014-2021 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +@testable +import Build + +import class Basics.ObservabilitySystem +import class TSCBasic.InMemoryFileSystem + +import class PackageModel.Manifest +import struct PackageModel.TargetDescription + +@testable +import struct PackageGraph.ResolvedProduct + +import func SPMTestSupport.loadPackageGraph +import func SPMTestSupport.mockBuildParameters +import func SPMTestSupport.XCTAssertNoDiagnostics +import XCTest + +final class ProductBuildDescriptionTests: XCTestCase { + func testEmbeddedProducts() throws { + let fs = InMemoryFileSystem( + emptyFiles: + "/Pkg/Sources/exe/main.swift" + ) + + let observability = ObservabilitySystem.makeForTesting() + let graph = try loadPackageGraph( + fileSystem: fs, + manifests: [ + Manifest.createRootManifest( + displayName: "Pkg", + path: "/Pkg", + targets: [ + TargetDescription( + name: "exe", + settings: [.init(tool: .swift, kind: .enableExperimentalFeature("Embedded"))] + ), + ] + ), + ], + observabilityScope: observability.topScope + ) + XCTAssertNoDiagnostics(observability.diagnostics) + + let id = ResolvedProduct.ID(productName: "exe", packageIdentity: .plain("pkg"), buildTriple: .destination) + let package = try XCTUnwrap(graph.rootPackages.first) + let product = try XCTUnwrap(graph.allProducts[id]) + + let buildDescription = try ProductBuildDescription( + package: package, + product: product, + toolsVersion: .v5_9, + buildParameters: mockBuildParameters(environment: .init(platform: .macOS)), + fileSystem: fs, + observabilityScope: observability.topScope + ) + + XCTAssertTrue( + try buildDescription.linkArguments() + .joined(separator: " ") + .contains("-enable-experimental-feature Embedded") + ) + } +} From 98747d519cab0a5e0a1610ccf8d558cbd4099454 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Wed, 21 Feb 2024 13:31:00 +0000 Subject: [PATCH 015/159] Fix `.wasm` product paths for `wasm32-unknown-none` triple (#7355) Absence of this extension on produced binaries makes it inconvenient to work with triple, as you have to add that extension manually after the fact. For examples, HTTP servers frequently don't infer correct mime-type for these binaries, which confuses browsers that try to load these binaries. --- Sources/Basics/Triple+Basics.swift | 10 +- .../ClangTargetBuildDescription.swift | 2 +- .../BuildParameters/BuildParameters.swift | 2 +- .../SPMTestSupport/MockPackageGraphs.swift | 158 ++++++++++++++++++ Tests/BuildTests/BuildPlanTests.swift | 105 ------------ .../CrossCompilationBuildPlanTests.swift | 152 +++++++++++++++++ .../LLBuildManifestBuilderTests.swift | 3 + .../ProductBuildDescriptionTests.swift | 2 +- .../PackageGraphTests/PackageGraphTests.swift | 3 +- 9 files changed, 325 insertions(+), 112 deletions(-) create mode 100644 Sources/SPMTestSupport/MockPackageGraphs.swift create mode 100644 Tests/BuildTests/CrossCompilationBuildPlanTests.swift diff --git a/Sources/Basics/Triple+Basics.swift b/Sources/Basics/Triple+Basics.swift index a7664bd39b9..12349f13009 100644 --- a/Sources/Basics/Triple+Basics.swift +++ b/Sources/Basics/Triple+Basics.swift @@ -24,6 +24,10 @@ extension Triple { } extension Triple { + public var isWasm: Bool { + [.wasm32, .wasm64].contains(self.arch) + } + public func isApple() -> Bool { vendor == .apple } @@ -148,6 +152,10 @@ extension Triple { } public var executableExtension: String { + guard !self.isWasm else { + return ".wasm" + } + guard let os = self.os else { return "" } @@ -157,8 +165,6 @@ extension Triple { return "" case .linux, .openbsd: return "" - case .wasi: - return ".wasm" case .win32: return ".exe" case .noneOS: diff --git a/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift b/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift index 69412b64fb3..495e9f2419a 100644 --- a/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift @@ -181,7 +181,7 @@ public final class ClangTargetBuildDescription { } } - /// An array of tuple containing filename, source, object and dependency path for each of the source in this target. + /// An array of tuples containing filename, source, object and dependency path for each of the source in this target. public func compilePaths() throws -> [(filename: RelativePath, source: AbsolutePath, object: AbsolutePath, deps: AbsolutePath)] { diff --git a/Sources/SPMBuildCore/BuildParameters/BuildParameters.swift b/Sources/SPMBuildCore/BuildParameters/BuildParameters.swift index cbc5cb5499c..e81e99e242f 100644 --- a/Sources/SPMBuildCore/BuildParameters/BuildParameters.swift +++ b/Sources/SPMBuildCore/BuildParameters/BuildParameters.swift @@ -260,7 +260,7 @@ public struct BuildParameters: Encodable { case .library(.automatic), .plugin: fatalError() case .test: - guard !self.triple.isWASI() else { + guard !self.triple.isWasm else { return try RelativePath(validating: "\(product.name).wasm") } switch testingParameters.library { diff --git a/Sources/SPMTestSupport/MockPackageGraphs.swift b/Sources/SPMTestSupport/MockPackageGraphs.swift new file mode 100644 index 00000000000..a068e54fb64 --- /dev/null +++ b/Sources/SPMTestSupport/MockPackageGraphs.swift @@ -0,0 +1,158 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import struct Basics.AbsolutePath +import class Basics.ObservabilitySystem +import class Basics.ObservabilityScope +import struct PackageGraph.PackageGraph +import class PackageModel.Manifest +import struct PackageModel.ProductDescription +import struct PackageModel.TargetDescription +import protocol TSCBasic.FileSystem +import class TSCBasic.InMemoryFileSystem + +@_spi(SwiftPMInternal) +public typealias MockPackageGraph = ( + graph: PackageGraph, + fileSystem: any FileSystem, + observabilityScope: ObservabilityScope +) + +@_spi(SwiftPMInternal) +public func macrosPackageGraph() throws -> MockPackageGraph { + let fs = InMemoryFileSystem(emptyFiles: + "/swift-firmware/Sources/Core/source.swift", + "/swift-firmware/Sources/HAL/source.swift", + "/swift-firmware/Tests/CoreTests/source.swift", + "/swift-firmware/Tests/HALTests/source.swift", + "/swift-mmio/Sources/MMIO/source.swift", + "/swift-mmio/Sources/MMIOMacros/source.swift", + "/swift-syntax/Sources/SwiftSyntax/source.swift", + "/swift-syntax/Tests/SwiftSyntaxTests/source.swift" + ) + + let observability = ObservabilitySystem.makeForTesting() + let graph = try loadPackageGraph( + fileSystem: fs, + manifests: [ + Manifest.createRootManifest( + displayName: "swift-firmware", + path: "/swift-firmware", + dependencies: [ + .localSourceControl( + path: "/swift-mmio", + requirement: .upToNextMajor(from: "1.0.0") + ) + ], + products: [ + ProductDescription( + name: "Core", + type: .executable, + targets: ["Core"] + ) + ], + targets: [ + TargetDescription( + name: "Core", + dependencies: ["HAL"], + type: .executable + ), + TargetDescription( + name: "HAL", + dependencies: [.product(name: "MMIO", package: "swift-mmio")] + ), + TargetDescription(name: "CoreTests", dependencies: ["Core"], type: .test), + TargetDescription(name: "HALTests", dependencies: ["HAL"], type: .test), + ] + ), + Manifest.createFileSystemManifest( + displayName: "swift-mmio", + path: "/swift-mmio", + dependencies: [ + .localSourceControl( + path: "/swift-syntax", + requirement: .upToNextMajor(from: "1.0.0") + ) + ], + products: [ + ProductDescription( + name: "MMIO", + type: .library(.automatic), + targets: ["MMIO"] + ) + ], + targets: [ + TargetDescription( + name: "MMIO", + dependencies: [.target(name: "MMIOMacros")] + ), + TargetDescription( + name: "MMIOMacros", + dependencies: [.product(name: "SwiftSyntax", package: "swift-syntax")], + type: .macro + ) + ] + ), + Manifest.createFileSystemManifest( + displayName: "swift-syntax", + path: "/swift-syntax", + products: [ + ProductDescription( + name: "SwiftSyntax", + type: .library(.automatic), + targets: ["SwiftSyntax"] + ) + ], + targets: [ + TargetDescription(name: "SwiftSyntax", dependencies: []), + TargetDescription(name: "SwiftSyntaxTests", dependencies: ["SwiftSyntax"], type: .test), + ] + ), + ], + observabilityScope: observability.topScope + ) + + XCTAssertNoDiagnostics(observability.diagnostics) + + return (graph, fs, observability.topScope) +} + +@_spi(SwiftPMInternal) +public func trivialPackageGraph(pkgRootPath: AbsolutePath) throws -> MockPackageGraph { + let fs = InMemoryFileSystem( + emptyFiles: + "/Pkg/Sources/app/main.swift", + "/Pkg/Sources/lib/lib.c", + "/Pkg/Sources/lib/include/lib.h", + "/Pkg/Tests/test/TestCase.swift" + ) + + let observability = ObservabilitySystem.makeForTesting() + let graph = try loadPackageGraph( + fileSystem: fs, + manifests: [ + Manifest.createRootManifest( + displayName: "Pkg", + path: "/Pkg", + targets: [ + TargetDescription(name: "app", dependencies: ["lib"]), + TargetDescription(name: "lib", dependencies: []), + TargetDescription(name: "test", dependencies: ["lib"], type: .test), + ] + ), + ], + observabilityScope: observability.topScope + ) + XCTAssertNoDiagnostics(observability.diagnostics) + + return (graph, fs, observability.topScope) +} diff --git a/Tests/BuildTests/BuildPlanTests.swift b/Tests/BuildTests/BuildPlanTests.swift index 2055e433f4b..6bf75fdc170 100644 --- a/Tests/BuildTests/BuildPlanTests.swift +++ b/Tests/BuildTests/BuildPlanTests.swift @@ -3431,111 +3431,6 @@ final class BuildPlanTests: XCTestCase { XCTAssertMatch(executablePathExtension, "exe") } - func testWASITarget() throws { - let Pkg: AbsolutePath = "/Pkg" - - let fs = InMemoryFileSystem( - emptyFiles: - Pkg.appending(components: "Sources", "app", "main.swift").pathString, - Pkg.appending(components: "Sources", "lib", "lib.c").pathString, - Pkg.appending(components: "Sources", "lib", "include", "lib.h").pathString, - Pkg.appending(components: "Tests", "test", "TestCase.swift").pathString - ) - - let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( - fileSystem: fs, - manifests: [ - Manifest.createRootManifest( - displayName: "Pkg", - path: .init(validating: Pkg.pathString), - targets: [ - TargetDescription(name: "app", dependencies: ["lib"]), - TargetDescription(name: "lib", dependencies: []), - TargetDescription(name: "test", dependencies: ["lib"], type: .test), - ] - ), - ], - observabilityScope: observability.topScope - ) - XCTAssertNoDiagnostics(observability.diagnostics) - - var parameters = mockBuildParameters(targetTriple: .wasi) - parameters.linkingParameters.shouldLinkStaticSwiftStdlib = true - let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: parameters, - graph: graph, - fileSystem: fs, - observabilityScope: observability.topScope - )) - result.checkProductsCount(2) - result - .checkTargetsCount(5) // There are two additional targets on non-Apple platforms, for test discovery and - // test entry point - - let buildPath = result.plan.productsBuildPath - - let lib = try result.target(for: "lib").clangTarget() - let args = [ - "-target", "wasm32-unknown-wasi", - "-O0", "-DSWIFT_PACKAGE=1", "-DDEBUG=1", - "-fblocks", - "-I", Pkg.appending(components: "Sources", "lib", "include").pathString, - "-g", - ] - XCTAssertEqual(try lib.basicArguments(isCXX: false), args) - XCTAssertEqual(try lib.objects, [buildPath.appending(components: "lib.build", "lib.c.o")]) - XCTAssertEqual(lib.moduleMap, buildPath.appending(components: "lib.build", "module.modulemap")) - - let exe = try result.target(for: "app").swiftTarget().compileArguments() - XCTAssertMatch( - exe, - [ - "-swift-version", "4", "-enable-batch-mode", "-Onone", "-enable-testing", - .equal(self.j), "-DSWIFT_PACKAGE", "-DDEBUG", "-Xcc", - "-fmodule-map-file=\(buildPath.appending(components: "lib.build", "module.modulemap"))", - "-Xcc", "-I", "-Xcc", "\(Pkg.appending(components: "Sources", "lib", "include"))", - "-module-cache-path", "\(buildPath.appending(components: "ModuleCache"))", .anySequence, - "-g", .anySequence, - ] - ) - - let appBuildDescription = try result.buildProduct(for: "app") - XCTAssertEqual( - try appBuildDescription.linkArguments(), - [ - result.plan.destinationBuildParameters.toolchain.swiftCompilerPath.pathString, - "-L", buildPath.pathString, - "-o", buildPath.appending(components: "app.wasm").pathString, - "-module-name", "app", "-static-stdlib", "-emit-executable", - "@\(buildPath.appending(components: "app.product", "Objects.LinkFileList"))", - "-target", "wasm32-unknown-wasi", - "-g", - ] - ) - - let executablePathExtension = try appBuildDescription.binaryPath.extension - XCTAssertEqual(executablePathExtension, "wasm") - - let testBuildDescription = try result.buildProduct(for: "PkgPackageTests") - XCTAssertEqual( - try testBuildDescription.linkArguments(), - [ - result.plan.destinationBuildParameters.toolchain.swiftCompilerPath.pathString, - "-L", buildPath.pathString, - "-o", buildPath.appending(components: "PkgPackageTests.wasm").pathString, - "-module-name", "PkgPackageTests", - "-emit-executable", - "@\(buildPath.appending(components: "PkgPackageTests.product", "Objects.LinkFileList"))", - "-target", "wasm32-unknown-wasi", - "-g", - ] - ) - - let testPathExtension = try testBuildDescription.binaryPath.extension - XCTAssertEqual(testPathExtension, "wasm") - } - func testEntrypointRenaming() throws { let fs = InMemoryFileSystem( emptyFiles: diff --git a/Tests/BuildTests/CrossCompilationBuildPlanTests.swift b/Tests/BuildTests/CrossCompilationBuildPlanTests.swift new file mode 100644 index 00000000000..e492abfc35a --- /dev/null +++ b/Tests/BuildTests/CrossCompilationBuildPlanTests.swift @@ -0,0 +1,152 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import struct Basics.AbsolutePath +import class Basics.ObservabilitySystem +import class Build.BuildPlan +import class Build.ProductBuildDescription +import class Build.SwiftTargetBuildDescription +import struct Basics.Triple +import class PackageModel.Manifest +import struct PackageModel.TargetDescription +import func SPMTestSupport.loadPackageGraph + +@_spi(SwiftPMInternal) +import func SPMTestSupport.macrosPackageGraph + +import func SPMTestSupport.mockBuildParameters + +@_spi(SwiftPMInternal) +import func SPMTestSupport.trivialPackageGraph + +import struct SPMTestSupport.BuildPlanResult +import func SPMTestSupport.XCTAssertMatch +import func SPMTestSupport.XCTAssertNoDiagnostics +import class TSCBasic.InMemoryFileSystem + +import XCTest + +final class CrossCompilationBuildPlanTests: XCTestCase { + func testEmbeddedWasmTarget() throws { + let pkgPath = AbsolutePath("/Pkg") + let (graph, fs, observabilityScope) = try trivialPackageGraph(pkgRootPath: pkgPath) + + let triple = try Triple("wasm32-unknown-none-wasm") + var parameters = mockBuildParameters(targetTriple: triple) + parameters.linkingParameters.shouldLinkStaticSwiftStdlib = true + let result = try BuildPlanResult(plan: BuildPlan( + buildParameters: parameters, + graph: graph, + fileSystem: fs, + observabilityScope: observabilityScope + )) + result.checkProductsCount(2) + // There are two additional targets on non-Apple platforms, for test discovery and + // test entry point + result.checkTargetsCount(5) + + let buildPath = result.plan.productsBuildPath + let appBuildDescription = try result.buildProduct(for: "app") + XCTAssertEqual( + try appBuildDescription.linkArguments(), + [ + result.plan.destinationBuildParameters.toolchain.swiftCompilerPath.pathString, + "-L", buildPath.pathString, + "-o", buildPath.appending(components: "app.wasm").pathString, + "-module-name", "app", "-static-stdlib", "-emit-executable", + "@\(buildPath.appending(components: "app.product", "Objects.LinkFileList"))", + "-target", triple.tripleString, + "-g", + ] + ) + } + + func testWASITarget() throws { + let pkgPath = AbsolutePath("/Pkg") + + let (graph, fs, observabilityScope) = try trivialPackageGraph(pkgRootPath: pkgPath) + + var parameters = mockBuildParameters(targetTriple: .wasi) + parameters.linkingParameters.shouldLinkStaticSwiftStdlib = true + let result = try BuildPlanResult(plan: BuildPlan( + buildParameters: parameters, + graph: graph, + fileSystem: fs, + observabilityScope: observabilityScope + )) + result.checkProductsCount(2) + // There are two additional targets on non-Apple platforms, for test discovery and + // test entry point + result.checkTargetsCount(5) + + let buildPath = result.plan.productsBuildPath + + let lib = try result.target(for: "lib").clangTarget() + XCTAssertEqual(try lib.basicArguments(isCXX: false), [ + "-target", "wasm32-unknown-wasi", + "-O0", "-DSWIFT_PACKAGE=1", "-DDEBUG=1", + "-fblocks", + "-I", pkgPath.appending(components: "Sources", "lib", "include").pathString, + "-g", + ]) + XCTAssertEqual(try lib.objects, [buildPath.appending(components: "lib.build", "lib.c.o")]) + XCTAssertEqual(lib.moduleMap, buildPath.appending(components: "lib.build", "module.modulemap")) + + let exe = try result.target(for: "app").swiftTarget().compileArguments() + XCTAssertMatch( + exe, + [ + "-swift-version", "4", "-enable-batch-mode", "-Onone", "-enable-testing", + "-j3", "-DSWIFT_PACKAGE", "-DDEBUG", "-Xcc", + "-fmodule-map-file=\(buildPath.appending(components: "lib.build", "module.modulemap"))", + "-Xcc", "-I", "-Xcc", "\(pkgPath.appending(components: "Sources", "lib", "include"))", + "-module-cache-path", "\(buildPath.appending(components: "ModuleCache"))", .anySequence, + "-g", .anySequence, + ] + ) + + let appBuildDescription = try result.buildProduct(for: "app") + XCTAssertEqual( + try appBuildDescription.linkArguments(), + [ + result.plan.destinationBuildParameters.toolchain.swiftCompilerPath.pathString, + "-L", buildPath.pathString, + "-o", buildPath.appending(components: "app.wasm").pathString, + "-module-name", "app", "-static-stdlib", "-emit-executable", + "@\(buildPath.appending(components: "app.product", "Objects.LinkFileList"))", + "-target", "wasm32-unknown-wasi", + "-g", + ] + ) + + let executablePathExtension = try appBuildDescription.binaryPath.extension + XCTAssertEqual(executablePathExtension, "wasm") + + let testBuildDescription = try result.buildProduct(for: "PkgPackageTests") + XCTAssertEqual( + try testBuildDescription.linkArguments(), + [ + result.plan.destinationBuildParameters.toolchain.swiftCompilerPath.pathString, + "-L", buildPath.pathString, + "-o", buildPath.appending(components: "PkgPackageTests.wasm").pathString, + "-module-name", "PkgPackageTests", + "-emit-executable", + "@\(buildPath.appending(components: "PkgPackageTests.product", "Objects.LinkFileList"))", + "-target", "wasm32-unknown-wasi", + "-g", + ] + ) + + let testPathExtension = try testBuildDescription.binaryPath.extension + XCTAssertEqual(testPathExtension, "wasm") + } +} diff --git a/Tests/BuildTests/LLBuildManifestBuilderTests.swift b/Tests/BuildTests/LLBuildManifestBuilderTests.swift index d577e577c7a..cb4b8f6d9e9 100644 --- a/Tests/BuildTests/LLBuildManifestBuilderTests.swift +++ b/Tests/BuildTests/LLBuildManifestBuilderTests.swift @@ -16,7 +16,10 @@ import LLBuildManifest import PackageGraph import PackageModel import struct SPMBuildCore.BuildParameters + +@_spi(SwiftPMInternal) import SPMTestSupport + import class TSCBasic.InMemoryFileSystem import XCTest diff --git a/Tests/BuildTests/ProductBuildDescriptionTests.swift b/Tests/BuildTests/ProductBuildDescriptionTests.swift index 40d1c906c2c..932c1c56be9 100644 --- a/Tests/BuildTests/ProductBuildDescriptionTests.swift +++ b/Tests/BuildTests/ProductBuildDescriptionTests.swift @@ -53,7 +53,7 @@ final class ProductBuildDescriptionTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let id = ResolvedProduct.ID(productName: "exe", packageIdentity: .plain("pkg"), buildTriple: .destination) + let id = ResolvedProduct.ID(targetName: "exe", packageIdentity: .plain("pkg"), buildTriple: .destination) let package = try XCTUnwrap(graph.rootPackages.first) let product = try XCTUnwrap(graph.allProducts[id]) diff --git a/Tests/PackageGraphTests/PackageGraphTests.swift b/Tests/PackageGraphTests/PackageGraphTests.swift index 27af5fee6c4..ab86f67d6a8 100644 --- a/Tests/PackageGraphTests/PackageGraphTests.swift +++ b/Tests/PackageGraphTests/PackageGraphTests.swift @@ -19,8 +19,7 @@ import XCTest import struct TSCBasic.ByteString import class TSCBasic.InMemoryFileSystem -class PackageGraphTests: XCTestCase { - +final class PackageGraphTests: XCTestCase { func testBasic() throws { let fs = InMemoryFileSystem(emptyFiles: "/Foo/Sources/Foo/source.swift", From 61d6215e51f3f0354ce3feee45032474796239a6 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 22 Feb 2024 03:40:48 +0900 Subject: [PATCH 016/159] Enable `--gc-sections` for WebAssembly targets (#7356) Enable `--gc-sections` back for WebAssembly targets ### Motivation: We disabled `--gc-sections` for Wasm targets due to a lack of features in the object file format. However, we recently added the missing piece in wasm object file format, so we no longer have any reason to disable it. ### Modifications: This effectively reverts 3a366cc7fa5daf38476af6627a3881f04e50bb05. After https://github.com/apple/swift/pull/71768, we don't have any reason to disable `--gc-sections` for WebAssembly targets. Note that the new wasm segment flags are only supported by the latest LLVM and wasm-ld, but we can assume that toolchains or Swift SDKs for WebAssembly have wasm-ld built from the latest LLVM. ### Result: Allow dead code stripping at link time for WebAssembly targets. --- .../ProductBuildDescription.swift | 9 ----- .../CrossCompilationBuildPlanTests.swift | 33 +++++++++++++++++++ 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/Sources/Build/BuildDescription/ProductBuildDescription.swift b/Sources/Build/BuildDescription/ProductBuildDescription.swift index 2eec24fa713..efdc497f14b 100644 --- a/Sources/Build/BuildDescription/ProductBuildDescription.swift +++ b/Sources/Build/BuildDescription/ProductBuildDescription.swift @@ -122,15 +122,6 @@ public final class ProductBuildDescription: SPMBuildCore.ProductBuildDescription return ["-Xlinker", "-dead_strip"] } else if triple.isWindows() { return ["-Xlinker", "/OPT:REF"] - } else if triple.arch == .wasm32 { - // FIXME: wasm-ld strips data segments referenced through __start/__stop symbols - // during GC, and it removes Swift metadata sections like swift5_protocols - // We should add support of SHF_GNU_RETAIN-like flag for __attribute__((retain)) - // to LLVM and wasm-ld - // This workaround is required for not only WASI but also all WebAssembly triples - // using wasm-ld (e.g. wasm32-unknown-unknown). So this branch is conditioned by - // arch == .wasm32 - return [] } else { return ["-Xlinker", "--gc-sections"] } diff --git a/Tests/BuildTests/CrossCompilationBuildPlanTests.swift b/Tests/BuildTests/CrossCompilationBuildPlanTests.swift index e492abfc35a..be15e15af0e 100644 --- a/Tests/BuildTests/CrossCompilationBuildPlanTests.swift +++ b/Tests/BuildTests/CrossCompilationBuildPlanTests.swift @@ -70,6 +70,39 @@ final class CrossCompilationBuildPlanTests: XCTestCase { ) } + func testWasmTargetRelease() throws { + let pkgPath = AbsolutePath("/Pkg") + + let (graph, fs, observabilityScope) = try trivialPackageGraph(pkgRootPath: pkgPath) + + var parameters = mockBuildParameters( + config: .release, targetTriple: .wasi, linkerDeadStrip: true + ) + parameters.linkingParameters.shouldLinkStaticSwiftStdlib = true + let result = try BuildPlanResult(plan: BuildPlan( + buildParameters: parameters, + graph: graph, + fileSystem: fs, + observabilityScope: observabilityScope + )) + let buildPath = result.plan.productsBuildPath + + let appBuildDescription = try result.buildProduct(for: "app") + XCTAssertEqual( + try appBuildDescription.linkArguments(), + [ + result.plan.destinationBuildParameters.toolchain.swiftCompilerPath.pathString, + "-L", buildPath.pathString, + "-o", buildPath.appending(components: "app.wasm").pathString, + "-module-name", "app", "-static-stdlib", "-emit-executable", + "-Xlinker", "--gc-sections", + "@\(buildPath.appending(components: "app.product", "Objects.LinkFileList"))", + "-target", "wasm32-unknown-wasi", + "-g", + ] + ) + } + func testWASITarget() throws { let pkgPath = AbsolutePath("/Pkg") From 03bf6b7048033cc13eb30a4ca0d1a558eee91f47 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 22 Feb 2024 20:25:07 +0000 Subject: [PATCH 017/159] Clarify that Xcode issues are not tracked in `BUG_REPORT.yml` (#7324) We're seeing a large amount of issues created that are unrelated to this repository. We need to add a clear warning to our issue template that such issues will be closed. --- .github/ISSUE_TEMPLATE/BUG_REPORT.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/BUG_REPORT.yml b/.github/ISSUE_TEMPLATE/BUG_REPORT.yml index 0c1f448e454..290bf8768f4 100644 --- a/.github/ISSUE_TEMPLATE/BUG_REPORT.yml +++ b/.github/ISSUE_TEMPLATE/BUG_REPORT.yml @@ -2,6 +2,14 @@ name: Bug Report description: Something isn't working as expected labels: [bug] body: +- type: checkboxes + id: cat-preferences + attributes: + label: "Is it reproducible with SwiftPM command-line tools: `swift build`, `swift test`, `swift package` etc?" + description: "Issues related to closed-source software are not tracked by this repository and will be closed. For Xcode, please file a feedback at https://feedbackassistant.apple.com instead." + options: + - label: Confirmed reproduction steps with SwiftPM CLI. + required: true - type: textarea attributes: label: Description From 5bafc5abb082aabd5f3f6569e7b52e2c7ec46485 Mon Sep 17 00:00:00 2001 From: Ben Barham Date: Thu, 22 Feb 2024 12:53:50 -0800 Subject: [PATCH 018/159] Allow reading `Package.swift` with the executable bit set 786c5131ed5ec823d3be90ad31923123fcaccd56 added support for symlinks, but didn't handle `executableBlob` in the added `switch`. `executableBlob` is the returned type for a file with the executable bit set, so needs to be handled in the same way as a regular `blob`. --- Sources/SourceControl/GitRepository.swift | 2 +- Tests/SourceControlTests/GitRepositoryTests.swift | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/SourceControl/GitRepository.swift b/Sources/SourceControl/GitRepository.swift index 5185f444b5f..5661f838062 100644 --- a/Sources/SourceControl/GitRepository.swift +++ b/Sources/SourceControl/GitRepository.swift @@ -1105,7 +1105,7 @@ private class GitFileSystemView: FileSystem { case .symlink: let path = try repository.readLink(hash: hash) return try readFileContents(AbsolutePath(validating: path)) - case .blob: + case .blob, .executableBlob: return try self.repository.readBlob(hash: hash) default: throw InternalError("unsupported git entry type \(entry.type) at path \(path)") diff --git a/Tests/SourceControlTests/GitRepositoryTests.swift b/Tests/SourceControlTests/GitRepositoryTests.swift index 00dcc4870eb..d69d5b711d6 100644 --- a/Tests/SourceControlTests/GitRepositoryTests.swift +++ b/Tests/SourceControlTests/GitRepositoryTests.swift @@ -291,6 +291,7 @@ class GitRepositoryTests: XCTestCase { // Check read of a file. XCTAssertEqual(try view.readFileContents("/test-file-1.txt"), test1FileContents) XCTAssertEqual(try view.readFileContents("/subdir/test-file-2.txt"), test2FileContents) + XCTAssertEqual(try view.readFileContents("/test-file-3.sh"), test3FileContents) } } From 52870620e7b550b492086ae877d6eb9afd94c33f Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 23 Feb 2024 13:29:57 +0000 Subject: [PATCH 019/159] Exclude `XCBuildSupport` when building with CMake (#7358) XCBuildSupport is not available on non-Darwin platforms, especially Windows, where we have to have full CMake support at the moment. Maintaining XCBuildSupport in these unsupported configurations adds unnecessary overhead, especially in cases like https://github.com/apple/swift-package-manager/pull/7258, where we have to add new dependencies only when `XCBuildSupport` is available. We should exclude from CMake builds to reduce this maintenance overhead. --- Sources/CMakeLists.txt | 1 - Sources/Commands/CMakeLists.txt | 7 ++++-- .../Commands/PackageTools/DumpCommands.swift | 7 ++++++ .../PackageTools/SwiftPackageTool.swift | 3 +++ Sources/Commands/SwiftBuildTool.swift | 3 +++ Sources/CoreCommands/BuildSystemSupport.swift | 11 +++++++++ Sources/CoreCommands/CMakeLists.txt | 7 ++++-- Sources/XCBuildSupport/CMakeLists.txt | 24 ------------------- Sources/swift-bootstrap/CMakeLists.txt | 6 +++-- Sources/swift-bootstrap/main.swift | 7 ++++++ 10 files changed, 45 insertions(+), 31 deletions(-) delete mode 100644 Sources/XCBuildSupport/CMakeLists.txt diff --git a/Sources/CMakeLists.txt b/Sources/CMakeLists.txt index a8aa9f771bc..a148ae10fb8 100644 --- a/Sources/CMakeLists.txt +++ b/Sources/CMakeLists.txt @@ -37,4 +37,3 @@ add_subdirectory(swift-package) add_subdirectory(swift-run) add_subdirectory(swift-test) add_subdirectory(Workspace) -add_subdirectory(XCBuildSupport) diff --git a/Sources/Commands/CMakeLists.txt b/Sources/Commands/CMakeLists.txt index b20e318afad..eb8242ee3e0 100644 --- a/Sources/Commands/CMakeLists.txt +++ b/Sources/Commands/CMakeLists.txt @@ -59,8 +59,11 @@ target_link_libraries(Commands PUBLIC SourceControl TSCBasic TSCUtility - Workspace - XCBuildSupport) + Workspace) + +target_compile_definitions(Commands + PRIVATE DISABLE_XCBUILD_SUPPORT) + target_link_libraries(Commands PRIVATE DriverSupport $<$>:FoundationXML>) diff --git a/Sources/Commands/PackageTools/DumpCommands.swift b/Sources/Commands/PackageTools/DumpCommands.swift index 35845334ad2..3a569807e70 100644 --- a/Sources/Commands/PackageTools/DumpCommands.swift +++ b/Sources/Commands/PackageTools/DumpCommands.swift @@ -15,7 +15,10 @@ import Basics import CoreCommands import Foundation import PackageModel + +#if !DISABLE_XCBUILD_SUPPORT import XCBuildSupport +#endif struct DumpSymbolGraph: SwiftCommand { static let configuration = CommandConfiguration( @@ -136,6 +139,7 @@ struct DumpPIF: SwiftCommand { var preserveStructure: Bool = false func run(_ swiftTool: SwiftTool) throws { + #if !DISABLE_XCBUILD_SUPPORT let graph = try swiftTool.loadPackageGraph() let pif = try PIFBuilder.generatePIF( buildParameters: swiftTool.productsBuildParameters, @@ -144,6 +148,9 @@ struct DumpPIF: SwiftCommand { observabilityScope: swiftTool.observabilityScope, preservePIFModelStructure: preserveStructure) print(pif) + #else + fatalError("This subcommand is not available on the current platform") + #endif } var toolWorkspaceConfiguration: ToolWorkspaceConfiguration { diff --git a/Sources/Commands/PackageTools/SwiftPackageTool.swift b/Sources/Commands/PackageTools/SwiftPackageTool.swift index 61495a66a1b..ececa15cf65 100644 --- a/Sources/Commands/PackageTools/SwiftPackageTool.swift +++ b/Sources/Commands/PackageTools/SwiftPackageTool.swift @@ -20,7 +20,10 @@ import PackageModel import SourceControl import SPMBuildCore import Workspace + +#if !DISABLE_XCBUILD_SUPPORT import XCBuildSupport +#endif import enum TSCUtility.Diagnostics diff --git a/Sources/Commands/SwiftBuildTool.swift b/Sources/Commands/SwiftBuildTool.swift index 521eb0c9405..ed01e55084b 100644 --- a/Sources/Commands/SwiftBuildTool.swift +++ b/Sources/Commands/SwiftBuildTool.swift @@ -16,7 +16,10 @@ import Build import CoreCommands import PackageGraph import SPMBuildCore + +#if !DISABLE_XCBUILD_SUPPORT import XCBuildSupport +#endif import class TSCBasic.Process import var TSCBasic.stdoutStream diff --git a/Sources/CoreCommands/BuildSystemSupport.swift b/Sources/CoreCommands/BuildSystemSupport.swift index 1f2a45bbdc7..ec96b4170df 100644 --- a/Sources/CoreCommands/BuildSystemSupport.swift +++ b/Sources/CoreCommands/BuildSystemSupport.swift @@ -12,7 +12,10 @@ import Build import SPMBuildCore + +#if !DISABLE_XCBUILD_SUPPORT import XCBuildSupport +#endif import class Basics.ObservabilityScope import struct PackageGraph.PackageGraph @@ -60,6 +63,7 @@ private struct NativeBuildSystemFactory: BuildSystemFactory { } } +#if !DISABLE_XCBUILD_SUPPORT private struct XcodeBuildSystemFactory: BuildSystemFactory { let swiftTool: SwiftTool @@ -87,12 +91,19 @@ private struct XcodeBuildSystemFactory: BuildSystemFactory { ) } } +#endif extension SwiftTool { public var defaultBuildSystemProvider: BuildSystemProvider { + #if !DISABLE_XCBUILD_SUPPORT .init(providers: [ .native: NativeBuildSystemFactory(swiftTool: self), .xcode: XcodeBuildSystemFactory(swiftTool: self) ]) + #else + .init(providers: [ + .native: NativeBuildSystemFactory(swiftTool: self), + ]) + #endif } } diff --git a/Sources/CoreCommands/CMakeLists.txt b/Sources/CoreCommands/CMakeLists.txt index 13d446033e1..3d65ad22ce4 100644 --- a/Sources/CoreCommands/CMakeLists.txt +++ b/Sources/CoreCommands/CMakeLists.txt @@ -18,8 +18,11 @@ target_link_libraries(CoreCommands PUBLIC PackageGraph TSCBasic TSCUtility - Workspace - XCBuildSupport) + Workspace) + +target_compile_definitions(CoreCommands + PRIVATE DISABLE_XCBUILD_SUPPORT) + target_link_libraries(CoreCommands PRIVATE DriverSupport $<$>:FoundationXML>) diff --git a/Sources/XCBuildSupport/CMakeLists.txt b/Sources/XCBuildSupport/CMakeLists.txt deleted file mode 100644 index 178e0bc1ba7..00000000000 --- a/Sources/XCBuildSupport/CMakeLists.txt +++ /dev/null @@ -1,24 +0,0 @@ -# This source file is part of the Swift open source project -# -# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors -# Licensed under Apache License v2.0 with Runtime Library Exception -# -# See http://swift.org/LICENSE.txt for license information -# See http://swift.org/CONTRIBUTORS.txt for Swift project authors - -add_library(XCBuildSupport STATIC - PIF.swift - PIFBuilder.swift - XCBuildDelegate.swift - XCBuildMessage.swift - XCBuildOutputParser.swift - XcodeBuildSystem.swift) -target_link_libraries(XCBuildSupport PUBLIC - Build - TSCBasic - TSCUtility - PackageGraph -) - -set_target_properties(XCBuildSupport PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY}) diff --git a/Sources/swift-bootstrap/CMakeLists.txt b/Sources/swift-bootstrap/CMakeLists.txt index f2c40f4c8a0..6371d1a01d4 100644 --- a/Sources/swift-bootstrap/CMakeLists.txt +++ b/Sources/swift-bootstrap/CMakeLists.txt @@ -16,5 +16,7 @@ target_link_libraries(swift-bootstrap PRIVATE PackageLoading PackageModel TSCBasic - TSCUtility - XCBuildSupport) + TSCUtility) + +target_compile_definitions(swift-bootstrap + PRIVATE DISABLE_XCBUILD_SUPPORT) diff --git a/Sources/swift-bootstrap/main.swift b/Sources/swift-bootstrap/main.swift index b879f9a79bb..02ceae1ca1e 100644 --- a/Sources/swift-bootstrap/main.swift +++ b/Sources/swift-bootstrap/main.swift @@ -20,7 +20,10 @@ import PackageGraph import PackageLoading import PackageModel import SPMBuildCore + +#if !DISABLE_XCBUILD_SUPPORT import XCBuildSupport +#endif import struct TSCBasic.KeyedPair import func TSCBasic.topologicalSort @@ -322,6 +325,7 @@ struct SwiftBootstrapBuildTool: ParsableCommand { observabilityScope: self.observabilityScope ) case .xcode: + #if !DISABLE_XCBUILD_SUPPORT return try XcodeBuildSystem( buildParameters: buildParameters, packageGraphLoader: packageGraphLoader, @@ -330,6 +334,9 @@ struct SwiftBootstrapBuildTool: ParsableCommand { fileSystem: self.fileSystem, observabilityScope: self.observabilityScope ) + #else + fatalError("SwiftPM was built without XCBuild support") + #endif } } From 2a01f6ece29511df6abcea0828f0c7c4a2aa1541 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 23 Feb 2024 14:13:30 +0000 Subject: [PATCH 020/159] Use structured concurrency in `PackageLoadingTests` (#7359) This is a stripped down version of https://github.com/apple/swift-package-manager/pull/6624 that modifies only the test suite and doesn't make any substantial to `ManifestLoader.swift` other than providing an `async` overload in `extension ManifestLoaderProtocol` that uses `withCheckedThrowingContinuation` to bridge into the existing implementation. --- Package.swift | 4 - Sources/PackageLoading/ManifestLoader.swift | 33 +++ .../SPMTestSupport/MockManifestLoader.swift | 91 +++++---- Sources/SPMTestSupport/XCTAssertHelpers.swift | 29 +++ .../ManifestLoadingTests.swift | 92 --------- .../ManifestLoaderCacheTests.swift | 146 +++++++------ .../PackageLoadingTests/PDLoadingTests.swift | 31 +-- .../PD_4_0_LoadingTests.swift | 62 +++--- .../PD_4_2_LoadingTests.swift | 193 ++++++++---------- .../PD_5_0_LoadingTests.swift | 70 +++---- .../PD_5_11_LoadingTests.swift | 18 +- .../PD_5_2_LoadingTests.swift | 48 ++--- .../PD_5_3_LoadingTests.swift | 58 +++--- .../PD_5_4_LoadingTests.swift | 8 +- .../PD_5_5_LoadingTests.swift | 8 +- .../PD_5_6_LoadingTests.swift | 40 ++-- .../PD_5_7_LoadingTests.swift | 34 +-- .../PD_5_9_LoadingTests.swift | 10 +- .../PD_Next_LoadingTests.swift | 4 +- 19 files changed, 465 insertions(+), 514 deletions(-) delete mode 100644 Tests/PackageLoadingPerformanceTests/ManifestLoadingTests.swift diff --git a/Package.swift b/Package.swift index 7dcb89a018f..57b9d328950 100644 --- a/Package.swift +++ b/Package.swift @@ -608,10 +608,6 @@ let package = Package( dependencies: ["PackageLoading", "SPMTestSupport"], exclude: ["Inputs", "pkgconfigInputs"] ), - .testTarget( - name: "PackageLoadingPerformanceTests", - dependencies: ["PackageLoading", "SPMTestSupport"] - ), .testTarget( name: "PackageModelTests", dependencies: ["PackageModel", "SPMTestSupport"] diff --git a/Sources/PackageLoading/ManifestLoader.swift b/Sources/PackageLoading/ManifestLoader.swift index 1d8793b9f81..3ce676254e0 100644 --- a/Sources/PackageLoading/ManifestLoader.swift +++ b/Sources/PackageLoading/ManifestLoader.swift @@ -232,6 +232,39 @@ extension ManifestLoaderProtocol { completion(.failure(error)) } } + } + + public func load( + packagePath: AbsolutePath, + packageIdentity: PackageIdentity, + packageKind: PackageReference.Kind, + packageLocation: String, + packageVersion: (version: Version?, revision: String?)?, + currentToolsVersion: ToolsVersion, + identityResolver: IdentityResolver, + dependencyMapper: DependencyMapper, + fileSystem: FileSystem, + observabilityScope: ObservabilityScope, + delegateQueue: DispatchQueue, + callbackQueue: DispatchQueue + ) async throws -> Manifest { + try await withCheckedThrowingContinuation { + self.load( + packagePath: packagePath, + packageIdentity: packageIdentity, + packageKind: packageKind, + packageLocation: packageLocation, + packageVersion: packageVersion, + currentToolsVersion: currentToolsVersion, + identityResolver: identityResolver, + dependencyMapper: dependencyMapper, + fileSystem: fileSystem, + observabilityScope: observabilityScope, + delegateQueue: delegateQueue, + callbackQueue: callbackQueue, + completion: $0.resume(with:) + ) + } } } diff --git a/Sources/SPMTestSupport/MockManifestLoader.swift b/Sources/SPMTestSupport/MockManifestLoader.swift index f1b0ebfd56a..8775dedbfa5 100644 --- a/Sources/SPMTestSupport/MockManifestLoader.swift +++ b/Sources/SPMTestSupport/MockManifestLoader.swift @@ -18,6 +18,7 @@ import PackageGraph import func XCTest.XCTFail +import enum TSCBasic.ProcessEnv import struct TSCUtility.Version public enum MockManifestLoaderError: Swift.Error { @@ -88,7 +89,7 @@ extension ManifestLoader { dependencyMapper: DependencyMapper? = .none, fileSystem: FileSystem, observabilityScope: ObservabilityScope - ) throws -> Manifest{ + ) async throws -> Manifest{ let packageIdentity: PackageIdentity let packageLocation: String switch packageKind { @@ -109,27 +110,23 @@ extension ManifestLoader { // FIXME: placeholder packageLocation = identity.description } - return try temp_await { - self.load( - manifestPath: manifestPath, - manifestToolsVersion: manifestToolsVersion, - packageIdentity: packageIdentity, - packageKind: packageKind, - packageLocation: packageLocation, - packageVersion: nil, - identityResolver: identityResolver, - dependencyMapper: dependencyMapper ?? DefaultDependencyMapper(identityResolver: identityResolver), - fileSystem: fileSystem, - observabilityScope: observabilityScope, - delegateQueue: .sharedConcurrent, - callbackQueue: .sharedConcurrent, - completion: $0 - ) - } + return try await self.load( + manifestPath: manifestPath, + manifestToolsVersion: manifestToolsVersion, + packageIdentity: packageIdentity, + packageKind: packageKind, + packageLocation: packageLocation, + packageVersion: nil, + identityResolver: identityResolver, + dependencyMapper: dependencyMapper ?? DefaultDependencyMapper(identityResolver: identityResolver), + fileSystem: fileSystem, + observabilityScope: observabilityScope, + delegateQueue: .sharedConcurrent, + callbackQueue: .sharedConcurrent + ) } } - extension ManifestLoader { public func load( packagePath: AbsolutePath, @@ -139,7 +136,7 @@ extension ManifestLoader { dependencyMapper: DependencyMapper? = .none, fileSystem: FileSystem, observabilityScope: ObservabilityScope - ) throws -> Manifest{ + ) async throws -> Manifest{ let packageIdentity: PackageIdentity let packageLocation: String switch packageKind { @@ -160,22 +157,44 @@ extension ManifestLoader { // FIXME: placeholder packageLocation = identity.description } - return try temp_await { - self.load( - packagePath: packagePath, - packageIdentity: packageIdentity, - packageKind: packageKind, - packageLocation: packageLocation, - packageVersion: nil, - currentToolsVersion: currentToolsVersion, - identityResolver: identityResolver, - dependencyMapper: dependencyMapper ?? DefaultDependencyMapper(identityResolver: identityResolver), - fileSystem: fileSystem, - observabilityScope: observabilityScope, - delegateQueue: .sharedConcurrent, - callbackQueue: .sharedConcurrent, - completion: $0 - ) + return try await self.load( + packagePath: packagePath, + packageIdentity: packageIdentity, + packageKind: packageKind, + packageLocation: packageLocation, + packageVersion: nil, + currentToolsVersion: currentToolsVersion, + identityResolver: identityResolver, + dependencyMapper: dependencyMapper ?? DefaultDependencyMapper(identityResolver: identityResolver), + fileSystem: fileSystem, + observabilityScope: observabilityScope, + delegateQueue: .sharedConcurrent, + callbackQueue: .sharedConcurrent + ) + } +} + +/// Temporary override environment variables +/// +/// WARNING! This method is not thread-safe. POSIX environments are shared +/// between threads. This means that when this method is called simultaneously +/// from different threads, the environment will neither be setup nor restored +/// correctly. +public func withCustomEnv(_ env: [String: String], body: () async throws -> Void) async throws { + let state = env.map { ($0, $1) } + let restore = { + for (key, value) in state { + try ProcessEnv.setVar(key, value: value) + } + } + do { + for (key, value) in env { + try ProcessEnv.setVar(key, value: value) } + try await body() + } catch { + try? restore() + throw error } + try restore() } diff --git a/Sources/SPMTestSupport/XCTAssertHelpers.swift b/Sources/SPMTestSupport/XCTAssertHelpers.swift index a0fcf5e5360..c771481b072 100644 --- a/Sources/SPMTestSupport/XCTAssertHelpers.swift +++ b/Sources/SPMTestSupport/XCTAssertHelpers.swift @@ -187,6 +187,35 @@ public func XCTAssertThrowsCommandExecutionError( } } +public func XCTAssertAsyncEqual( + _ expression1: @autoclosure () async throws -> T, + _ expression2: @autoclosure () async throws -> T, + _ message: @autoclosure () -> String = "", + file: StaticString = #file, + line: UInt = #line +) async rethrows { + let value1 = try await expression1() + let value2 = try await expression2() + + XCTAssertEqual(value1, value2, message(), file: file, line: line) +} + +struct XCAsyncTestErrorWhileUnwrappingOptional: Error {} + +public func XCTAsyncUnwrap( + _ expression: @autoclosure () async throws -> T?, + _ message: @autoclosure () -> String = "", + file: StaticString = #filePath, + line: UInt = #line +) async throws -> T { + guard let result = try await expression() else { + throw XCAsyncTestErrorWhileUnwrappingOptional() + } + + return result +} + + public struct CommandExecutionError: Error { public let result: ProcessResult public let stdout: String diff --git a/Tests/PackageLoadingPerformanceTests/ManifestLoadingTests.swift b/Tests/PackageLoadingPerformanceTests/ManifestLoadingTests.swift deleted file mode 100644 index ba586cc322a..00000000000 --- a/Tests/PackageLoadingPerformanceTests/ManifestLoadingTests.swift +++ /dev/null @@ -1,92 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift open source project -// -// Copyright (c) 2014-2017 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See http://swift.org/LICENSE.txt for license information -// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import Basics -import PackageModel -import PackageLoading -import SPMTestSupport -import XCTest - -import class TSCTestSupport.XCTestCasePerf - -class ManifestLoadingPerfTests: XCTestCasePerf { - let manifestLoader = ManifestLoader(toolchain: try! UserToolchain.default) - - func write(_ content: String, body: (AbsolutePath) -> ()) throws { - try testWithTemporaryDirectory { tmpdir in - let manifestFile = tmpdir.appending("Package.swift") - try localFileSystem.writeFileContents(manifestFile, string: content) - body(tmpdir) - } - } - - func testTrivialManifestLoading_X1() throws { - #if !os(macOS) - try XCTSkipIf(true, "test is only supported on macOS") - #endif - let N = 1 - let trivialManifest = """ - import PackageDescription - let package = Package(name: "Trivial") - """ - - try write(trivialManifest) { path in - measure { - for _ in 0.. [ManifestLoader.CacheKey: ManifestLoader.EvaluationResult] { +private func makeMockManifests( + fileSystem: FileSystem, + rootPath: AbsolutePath, + count: Int = Int.random(in: 50 ..< 100) +) throws -> [ManifestLoader.CacheKey: ManifestLoader.EvaluationResult] { var manifests = [ManifestLoader.CacheKey: ManifestLoader.EvaluationResult]() for index in 0 ..< count { let packagePath = rootPath.appending("\(index)") diff --git a/Tests/PackageLoadingTests/PDLoadingTests.swift b/Tests/PackageLoadingTests/PDLoadingTests.swift index 37a46c17a3c..aa2bfd7af44 100644 --- a/Tests/PackageLoadingTests/PDLoadingTests.swift +++ b/Tests/PackageLoadingTests/PDLoadingTests.swift @@ -66,8 +66,8 @@ class PackageDescriptionLoadingTests: XCTestCase, ManifestLoaderDelegate { observabilityScope: ObservabilityScope, file: StaticString = #file, line: UInt = #line - ) throws -> (manifest: Manifest, diagnostics: [Basics.Diagnostic]) { - try Self.loadAndValidateManifest( + ) async throws -> (manifest: Manifest, diagnostics: [Basics.Diagnostic]) { + try await Self.loadAndValidateManifest( content, toolsVersion: toolsVersion ?? self.toolsVersion, packageKind: packageKind ?? .fileSystem(.root), @@ -86,7 +86,7 @@ class PackageDescriptionLoadingTests: XCTestCase, ManifestLoaderDelegate { observabilityScope: ObservabilityScope, file: StaticString = #file, line: UInt = #line - ) throws -> (manifest: Manifest, diagnostics: [Basics.Diagnostic]) { + ) async throws -> (manifest: Manifest, diagnostics: [Basics.Diagnostic]) { let packagePath: AbsolutePath switch packageKind { case .root(let path): @@ -103,7 +103,7 @@ class PackageDescriptionLoadingTests: XCTestCase, ManifestLoaderDelegate { let fileSystem = InMemoryFileSystem() let manifestPath = packagePath.appending(component: Manifest.filename) try fileSystem.writeFileContents(manifestPath, string: content) - let manifest = try manifestLoader.load( + let manifest = try await manifestLoader.load( manifestPath: manifestPath, packageKind: packageKind, toolsVersion: toolsVersion, @@ -124,15 +124,6 @@ class PackageDescriptionLoadingTests: XCTestCase, ManifestLoaderDelegate { final class ManifestTestDelegate: ManifestLoaderDelegate { private let loaded = ThreadSafeArrayStore() private let parsed = ThreadSafeArrayStore() - private let loadingGroup = DispatchGroup() - private let parsingGroup = DispatchGroup() - - func prepare(expectParsing: Bool = true) { - self.loadingGroup.enter() - if expectParsing { - self.parsingGroup.enter() - } - } func willLoad(packageIdentity: PackageModel.PackageIdentity, packageLocation: String, manifestPath: AbsolutePath) { // noop @@ -140,7 +131,6 @@ final class ManifestTestDelegate: ManifestLoaderDelegate { func didLoad(packageIdentity: PackageIdentity, packageLocation: String, manifestPath: AbsolutePath, duration: DispatchTimeInterval) { self.loaded.append(manifestPath) - self.loadingGroup.leave() } func willParse(packageIdentity: PackageIdentity, packageLocation: String) { @@ -165,7 +155,6 @@ final class ManifestTestDelegate: ManifestLoaderDelegate { func didEvaluate(packageIdentity: PackageIdentity, packageLocation: String, manifestPath: AbsolutePath, duration: DispatchTimeInterval) { self.parsed.append(manifestPath) - self.parsingGroup.leave() } @@ -174,17 +163,13 @@ final class ManifestTestDelegate: ManifestLoaderDelegate { self.parsed.clear() } - func loaded(timeout: DispatchTime) throws -> [AbsolutePath] { - guard case .success = self.loadingGroup.wait(timeout: timeout) else { - throw StringError("timeout waiting for loading") - } + func loaded(timeout: Duration) async throws -> [AbsolutePath] { + try await Task.sleep(for: timeout) return self.loaded.get() } - func parsed(timeout: DispatchTime) throws -> [AbsolutePath] { - guard case .success = self.parsingGroup.wait(timeout: timeout) else { - throw StringError("timeout waiting for parsing") - } + func parsed(timeout: Duration) async throws -> [AbsolutePath] { + try await Task.sleep(for: timeout) return self.parsed.get() } } diff --git a/Tests/PackageLoadingTests/PD_4_0_LoadingTests.swift b/Tests/PackageLoadingTests/PD_4_0_LoadingTests.swift index 435c8025a27..365985a8618 100644 --- a/Tests/PackageLoadingTests/PD_4_0_LoadingTests.swift +++ b/Tests/PackageLoadingTests/PD_4_0_LoadingTests.swift @@ -18,12 +18,12 @@ import XCTest import class TSCBasic.InMemoryFileSystem -class PackageDescription4_0LoadingTests: PackageDescriptionLoadingTests { +final class PackageDescription4_0LoadingTests: PackageDescriptionLoadingTests { override var toolsVersion: ToolsVersion { .v4 } - func testTrivial() throws { + func testTrivial() async throws { let content = """ import PackageDescription let package = Package( @@ -32,7 +32,7 @@ class PackageDescription4_0LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -42,7 +42,7 @@ class PackageDescription4_0LoadingTests: PackageDescriptionLoadingTests { XCTAssertEqual(manifest.dependencies, []) } - func testTargetDependencies() throws { + func testTargetDependencies() async throws { let content = """ import PackageDescription let package = Package( @@ -62,7 +62,7 @@ class PackageDescription4_0LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -86,7 +86,7 @@ class PackageDescription4_0LoadingTests: PackageDescriptionLoadingTests { XCTAssertEqual(bar.dependencies, ["foo"]) } - func testCompatibleSwiftVersions() throws { + func testCompatibleSwiftVersions() async throws { do { let content = """ import PackageDescription @@ -96,7 +96,7 @@ class PackageDescription4_0LoadingTests: PackageDescriptionLoadingTests { ) """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) XCTAssertEqual(manifest.swiftLanguageVersions?.map({$0.rawValue}), ["3", "4"]) @@ -111,7 +111,7 @@ class PackageDescription4_0LoadingTests: PackageDescriptionLoadingTests { ) """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) XCTAssertEqual(manifest.swiftLanguageVersions, []) @@ -124,14 +124,14 @@ class PackageDescription4_0LoadingTests: PackageDescriptionLoadingTests { name: "Foo") """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) XCTAssertEqual(manifest.swiftLanguageVersions, nil) } } - func testPackageDependencies() throws { + func testPackageDependencies() async throws { let content = """ import PackageDescription let package = Package( @@ -148,7 +148,7 @@ class PackageDescription4_0LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -161,7 +161,7 @@ class PackageDescription4_0LoadingTests: PackageDescriptionLoadingTests { XCTAssertEqual(deps["foo6"], .localSourceControl(path: "/foo6", requirement: .revision("58e9de4e7b79e67c72a46e164158e3542e570ab6"))) } - func testProducts() throws { + func testProducts() async throws { let content = """ import PackageDescription let package = Package( @@ -179,7 +179,7 @@ class PackageDescription4_0LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -201,7 +201,7 @@ class PackageDescription4_0LoadingTests: PackageDescriptionLoadingTests { XCTAssertEqual(fooDy.targets, ["Foo"]) } - func testSystemPackage() throws { + func testSystemPackage() async throws { let content = """ import PackageDescription let package = Package( @@ -215,7 +215,7 @@ class PackageDescription4_0LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -227,7 +227,7 @@ class PackageDescription4_0LoadingTests: PackageDescriptionLoadingTests { ]) } - func testCTarget() throws { + func testCTarget() async throws { let content = """ import PackageDescription let package = Package( @@ -243,7 +243,7 @@ class PackageDescription4_0LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -254,7 +254,7 @@ class PackageDescription4_0LoadingTests: PackageDescriptionLoadingTests { XCTAssertEqual(bar.publicHeadersPath, nil) } - func testTargetProperties() throws { + func testTargetProperties() async throws { let content = """ import PackageDescription let package = Package( @@ -273,7 +273,7 @@ class PackageDescription4_0LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -290,7 +290,7 @@ class PackageDescription4_0LoadingTests: PackageDescriptionLoadingTests { XCTAssert(bar.sources == nil) } - func testUnavailableAPIs() throws { + func testUnavailableAPIs() async throws { let content = """ import PackageDescription let package = Package( @@ -305,7 +305,7 @@ class PackageDescription4_0LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in if case ManifestParseError.invalidManifestFormat(let error, _, _) = error { XCTAssert(error.contains("error: 'package(url:version:)' is unavailable: use package(url:exact:) instead"), "\(error)") XCTAssert(error.contains("error: 'package(url:range:)' is unavailable: use package(url:_:) instead"), "\(error)") @@ -315,7 +315,7 @@ class PackageDescription4_0LoadingTests: PackageDescriptionLoadingTests { } } - func testLanguageStandards() throws { + func testLanguageStandards() async throws { let content = """ import PackageDescription let package = Package( @@ -329,7 +329,7 @@ class PackageDescription4_0LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -338,7 +338,7 @@ class PackageDescription4_0LoadingTests: PackageDescriptionLoadingTests { XCTAssertEqual(manifest.cxxLanguageStandard, "gnu++14") } - func testManifestWithWarnings() throws { + func testManifestWithWarnings() async throws { let fs = InMemoryFileSystem() let manifestPath = AbsolutePath.root.appending(component: Manifest.filename) @@ -355,7 +355,7 @@ class PackageDescription4_0LoadingTests: PackageDescriptionLoadingTests { try fs.writeFileContents(manifestPath, string: content) let observability = ObservabilitySystem.makeForTesting() - let manifest = try manifestLoader.load( + let manifest = try await manifestLoader.load( manifestPath: manifestPath, packageKind: .root(.root), toolsVersion: .v4, @@ -373,7 +373,7 @@ class PackageDescription4_0LoadingTests: PackageDescriptionLoadingTests { } } - func testDuplicateTargets() throws { + func testDuplicateTargets() async throws { let content = """ import PackageDescription @@ -389,7 +389,7 @@ class PackageDescription4_0LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (_, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (_, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) testDiagnostics(validationDiagnostics) { result in result.checkUnordered(diagnostic: "duplicate target named 'A'", severity: .error) @@ -397,7 +397,7 @@ class PackageDescription4_0LoadingTests: PackageDescriptionLoadingTests { } } - func testEmptyProductTargets() throws { + func testEmptyProductTargets() async throws { let content = """ import PackageDescription @@ -413,14 +413,14 @@ class PackageDescription4_0LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (_, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (_, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) testDiagnostics(validationDiagnostics) { result in result.check(diagnostic: "product 'Product' doesn't reference any targets", severity: .error) } } - func testProductTargetNotFound() throws { + func testProductTargetNotFound() async throws { let content = """ import PackageDescription @@ -436,7 +436,7 @@ class PackageDescription4_0LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (_, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (_, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) testDiagnostics(validationDiagnostics) { result in result.check(diagnostic: "target 'B' referenced in product 'Product' could not be found; valid targets are: 'A'", severity: .error) diff --git a/Tests/PackageLoadingTests/PD_4_2_LoadingTests.swift b/Tests/PackageLoadingTests/PD_4_2_LoadingTests.swift index ddd8f7e402a..d460fb6be9a 100644 --- a/Tests/PackageLoadingTests/PD_4_2_LoadingTests.swift +++ b/Tests/PackageLoadingTests/PD_4_2_LoadingTests.swift @@ -24,12 +24,13 @@ import func TSCTestSupport.withCustomEnv import struct TSCUtility.Version -class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests { +@available(macOS 13, iOS 16, tvOS 16, watchOS 9, *) +final class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests { override var toolsVersion: ToolsVersion { .v4_2 } - func testBasics() throws { + func testBasics() async throws { let content = """ import PackageDescription let package = Package( @@ -55,7 +56,7 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -90,7 +91,7 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests { XCTAssertEqual(fooProduct.targets, ["foo"]) } - func testSwiftLanguageVersions() throws { + func testSwiftLanguageVersions() async throws { // Ensure integer values are not accepted. do { let content = """ @@ -102,7 +103,7 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in if case ManifestParseError.invalidManifestFormat(let message, _, _) = error { XCTAssertMatch( message, @@ -128,7 +129,7 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -145,7 +146,7 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -165,7 +166,7 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in if case ManifestParseError.invalidManifestFormat(let message, _, _) = error { XCTAssertMatch(message, .contains("is unavailable")) XCTAssertMatch(message, .contains("was introduced in PackageDescription 5")) @@ -176,7 +177,7 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests { } } - func testPlatforms() throws { + func testPlatforms() async throws { do { let content = """ import PackageDescription @@ -187,7 +188,7 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in if case ManifestParseError.invalidManifestFormat(let message, _, _) = error { XCTAssertMatch(message, .contains("is unavailable")) XCTAssertMatch(message, .contains("was introduced in PackageDescription 5")) @@ -207,7 +208,7 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in if case ManifestParseError.invalidManifestFormat(let message, _, _) = error { XCTAssertMatch(message, .contains("is unavailable")) XCTAssertMatch(message, .contains("was introduced in PackageDescription 5")) @@ -218,7 +219,7 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests { } } - func testBuildSettings() throws { + func testBuildSettings() async throws { let content = """ import PackageDescription let package = Package( @@ -238,7 +239,7 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in if case ManifestParseError.invalidManifestFormat(let message, _, _) = error { XCTAssertMatch(message, .contains("is unavailable")) XCTAssertMatch(message, .contains("was introduced in PackageDescription 5")) @@ -248,7 +249,7 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests { } } - func testPackageDependencies() throws { + func testPackageDependencies() async throws { let content = """ import PackageDescription let package = Package( @@ -273,7 +274,7 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -331,7 +332,7 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests { } } - func testSystemLibraryTargets() throws { + func testSystemLibraryTargets() async throws { let content = """ import PackageDescription let package = Package( @@ -352,7 +353,7 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -371,7 +372,7 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests { /// Check that we load the manifest appropriate for the current version, if /// version specific customization is used. - func testVersionSpecificLoading() throws { + func testVersionSpecificLoading() async throws { let bogusManifest = "THIS WILL NOT PARSE" let trivialManifest = """ @@ -405,7 +406,7 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests { ) } // Check we can load the repository. - let manifest = try manifestLoader.load( + let manifest = try await manifestLoader.load( packagePath: root, packageKind: .root(.root), currentToolsVersion: .v4_2, @@ -417,7 +418,7 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests { } // Check that ancient `Package@swift-3.swift` manifests are properly treated as 3.1 even without a tools-version comment. - func testVersionSpecificLoadingOfVersion3Manifest() throws { + func testVersionSpecificLoadingOfVersion3Manifest() async throws { // Create a temporary FS to hold the package manifests. let fs = InMemoryFileSystem() let observability = ObservabilitySystem.makeForTesting() @@ -434,7 +435,7 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests { string: manifestContents ) // Check we can load the manifest. - let manifest = try manifestLoader.load(packagePath: packageDir, packageKind: .root(packageDir), currentToolsVersion: .v4_2, fileSystem: fs, observabilityScope: observability.topScope) + let manifest = try await manifestLoader.load(packagePath: packageDir, packageKind: .root(packageDir), currentToolsVersion: .v4_2, fileSystem: fs, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertEqual(manifest.displayName, "Trivial") @@ -448,12 +449,12 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests { string: "// swift-tools-version:4.0\n" + manifestContents ) // Check we can load the manifest. - let manifest2 = try manifestLoader.load(packagePath: packageDir, packageKind: .root(packageDir), currentToolsVersion: .v4_2, fileSystem: fs, observabilityScope: observability.topScope) + let manifest2 = try await manifestLoader.load(packagePath: packageDir, packageKind: .root(packageDir), currentToolsVersion: .v4_2, fileSystem: fs, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertEqual(manifest2.displayName, "Trivial") } - func testRuntimeManifestErrors() throws { + func testRuntimeManifestErrors() async throws { let content = """ import PackageDescription let package = Package( @@ -477,7 +478,7 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in if case ManifestParseError.runtimeManifestErrors(let errors) = error { XCTAssertEqual(errors, ["Invalid semantic version string '1.0,0'"]) } else { @@ -486,7 +487,7 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests { } } - func testNotAbsoluteDependencyPath() throws { + func testNotAbsoluteDependencyPath() async throws { let content = """ import PackageDescription let package = Package( @@ -503,7 +504,7 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in if case ManifestParseError.invalidManifestFormat(let message, let diagnosticFile, _) = error { XCTAssertNil(diagnosticFile) XCTAssertEqual(message, "'https://someurl.com' is not a valid path for path-based dependencies; use relative or absolute path instead.") @@ -513,7 +514,7 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests { } } - func testFileURLErrors() throws { + func testFileURLErrors() async throws { enum ExpectedError { case invalidAbsolutePath case relativePath @@ -563,7 +564,7 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in switch error { case is ManifestParseError: XCTAssertEqual(error as? ManifestParseError, expectedError.manifestError) @@ -576,7 +577,7 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests { } } - func testProductTargetNotFound() throws { + func testProductTargetNotFound() async throws { let content = """ import PackageDescription @@ -594,7 +595,7 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (_, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (_, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) testDiagnostics(validationDiagnostics) { result in result.check(diagnostic: .contains("target 'B' referenced in product 'Product' could not be found; valid targets are: 'A', 'C', 'b'"), severity: .error) @@ -629,7 +630,6 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests { let dependencyMapper = DefaultDependencyMapper(identityResolver: identityResolver) // warm up caches - delegate.prepare() let manifest = try await manifestLoader.load( manifestPath: manifestPath, manifestToolsVersion: .v4_2, @@ -649,37 +649,28 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests { XCTAssertEqual(manifest.displayName, "Trivial") XCTAssertEqual(manifest.targets[0].name, "foo") - await withTaskGroup(of:Void.self) { group in - for _ in 0 ..< total { - delegate.prepare(expectParsing: false) - group.addTask { - do { - let manifest = try await manifestLoader.load( - manifestPath: manifestPath, - manifestToolsVersion: .v4_2, - packageIdentity: .plain("Trivial"), - packageKind: .fileSystem(manifestPath.parentDirectory), - packageLocation: manifestPath.pathString, - packageVersion: nil, - identityResolver: identityResolver, - dependencyMapper: dependencyMapper, - fileSystem: localFileSystem, - observabilityScope: observability.topScope, - delegateQueue: .sharedConcurrent, - callbackQueue: .sharedConcurrent - ) - XCTAssertNoDiagnostics(observability.diagnostics) - XCTAssertEqual(manifest.displayName, "Trivial") - XCTAssertEqual(manifest.targets[0].name, "foo") - } catch { - XCTFail("\(error)") - } - } - } - await group.waitForAll() + for _ in 0 ..< total { + let manifest = try await manifestLoader.load( + manifestPath: manifestPath, + manifestToolsVersion: .v4_2, + packageIdentity: .plain("Trivial"), + packageKind: .fileSystem(manifestPath.parentDirectory), + packageLocation: manifestPath.pathString, + packageVersion: nil, + identityResolver: identityResolver, + dependencyMapper: dependencyMapper, + fileSystem: localFileSystem, + observabilityScope: observability.topScope, + delegateQueue: .sharedConcurrent, + callbackQueue: .sharedConcurrent + ) + + XCTAssertNoDiagnostics(observability.diagnostics) + XCTAssertEqual(manifest.displayName, "Trivial") + XCTAssertEqual(manifest.targets[0].name, "foo") } - XCTAssertEqual(try delegate.loaded(timeout: .now() + 1).count, total+1) + try await XCTAssertAsyncEqual(try await delegate.loaded(timeout: .seconds(1)).count, total+1) XCTAssertFalse(observability.hasWarningDiagnostics, observability.diagnostics.description) XCTAssertFalse(observability.hasErrorDiagnostics, observability.diagnostics.description) } @@ -702,55 +693,47 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests { let identityResolver = DefaultIdentityResolver() let dependencyMapper = DefaultDependencyMapper(identityResolver: identityResolver) - try await withThrowingTaskGroup(of: Void.self) { group in - for _ in 0 ..< total { - let random = Int.random(in: 0 ... total / 4) - let manifestPath = path.appending(components: "pkg-\(random)", "Package.swift") - if !localFileSystem.exists(manifestPath) { - try localFileSystem.createDirectory(manifestPath.parentDirectory) - try localFileSystem.writeFileContents( - manifestPath, - string: """ - import PackageDescription - let package = Package( - name: "Trivial-\(random)", - targets: [ - .target( - name: "foo-\(random)", - dependencies: []), - ] - ) - """ + for _ in 0 ..< total { + let random = Int.random(in: 0 ... total / 4) + let manifestPath = path.appending(components: "pkg-\(random)", "Package.swift") + if !localFileSystem.exists(manifestPath) { + try localFileSystem.createDirectory(manifestPath.parentDirectory) + try localFileSystem.writeFileContents( + manifestPath, + string: """ + import PackageDescription + let package = Package( + name: "Trivial-\(random)", + targets: [ + .target( + name: "foo-\(random)", + dependencies: []), + ] ) - } - group.addTask { - do { - delegate.prepare() - let manifest = try await manifestLoader.load( - manifestPath: manifestPath, - manifestToolsVersion: .v4_2, - packageIdentity: .plain("Trivial-\(random)"), - packageKind: .fileSystem(manifestPath.parentDirectory), - packageLocation: manifestPath.pathString, - packageVersion: nil, - identityResolver: identityResolver, - dependencyMapper: dependencyMapper, - fileSystem: localFileSystem, - observabilityScope: observability.topScope, - delegateQueue: .sharedConcurrent, - callbackQueue: .sharedConcurrent - ) - XCTAssertEqual(manifest.displayName, "Trivial-\(random)") - XCTAssertEqual(manifest.targets[0].name, "foo-\(random)") - } catch { - XCTFail("\(error)") - } - } + """ + ) } - try await group.waitForAll() + + let manifest = try await manifestLoader.load( + manifestPath: manifestPath, + manifestToolsVersion: .v4_2, + packageIdentity: .plain("Trivial-\(random)"), + packageKind: .fileSystem(manifestPath.parentDirectory), + packageLocation: manifestPath.pathString, + packageVersion: nil, + identityResolver: identityResolver, + dependencyMapper: dependencyMapper, + fileSystem: localFileSystem, + observabilityScope: observability.topScope, + delegateQueue: .sharedConcurrent, + callbackQueue: .sharedConcurrent + ) + + XCTAssertEqual(manifest.displayName, "Trivial-\(random)") + XCTAssertEqual(manifest.targets[0].name, "foo-\(random)") } - XCTAssertEqual(try delegate.loaded(timeout: .now() + 1).count, total) + try await XCTAssertAsyncEqual(try await delegate.loaded(timeout: .seconds(1)).count, total) XCTAssertFalse(observability.hasWarningDiagnostics, observability.diagnostics.description) XCTAssertFalse(observability.hasErrorDiagnostics, observability.diagnostics.description) } diff --git a/Tests/PackageLoadingTests/PD_5_0_LoadingTests.swift b/Tests/PackageLoadingTests/PD_5_0_LoadingTests.swift index 18873299874..7cc3c97e863 100644 --- a/Tests/PackageLoadingTests/PD_5_0_LoadingTests.swift +++ b/Tests/PackageLoadingTests/PD_5_0_LoadingTests.swift @@ -18,12 +18,12 @@ import XCTest import struct TSCBasic.ByteString -class PackageDescription5_0LoadingTests: PackageDescriptionLoadingTests { +final class PackageDescription5_0LoadingTests: PackageDescriptionLoadingTests { override var toolsVersion: ToolsVersion { .v5 } - func testBasics() throws { + func testBasics() async throws { let content = """ import PackageDescription let package = Package( @@ -49,7 +49,7 @@ class PackageDescription5_0LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -84,7 +84,7 @@ class PackageDescription5_0LoadingTests: PackageDescriptionLoadingTests { XCTAssertEqual(fooProduct.targets, ["foo"]) } - func testSwiftLanguageVersion() throws { + func testSwiftLanguageVersion() async throws { do { let content = """ import PackageDescription @@ -95,7 +95,7 @@ class PackageDescription5_0LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -112,7 +112,7 @@ class PackageDescription5_0LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in if case ManifestParseError.invalidManifestFormat(let message, _, _) = error { XCTAssertMatch(message, .contains("'v3' is unavailable")) XCTAssertMatch(message, .contains("'v3' was obsoleted in PackageDescription 5")) @@ -132,7 +132,7 @@ class PackageDescription5_0LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in if case ManifestParseError.runtimeManifestErrors(let messages) = error { XCTAssertEqual(messages, ["invalid Swift language version: "]) } else { @@ -142,7 +142,7 @@ class PackageDescription5_0LoadingTests: PackageDescriptionLoadingTests { } } - func testPlatformOptions() throws { + func testPlatformOptions() async throws { let content = """ import PackageDescription let package = Package( @@ -155,7 +155,7 @@ class PackageDescription5_0LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -166,7 +166,7 @@ class PackageDescription5_0LoadingTests: PackageDescriptionLoadingTests { ]) } - func testPlatforms() throws { + func testPlatforms() async throws { do { let content = """ import PackageDescription @@ -180,7 +180,7 @@ class PackageDescription5_0LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -205,7 +205,7 @@ class PackageDescription5_0LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in if case ManifestParseError.runtimeManifestErrors(let errors) = error { XCTAssertEqual(errors, [ "invalid macOS version -11.2; -11 should be a positive integer", @@ -232,7 +232,7 @@ class PackageDescription5_0LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in if case ManifestParseError.runtimeManifestErrors(let errors) = error { XCTAssertEqual(errors, ["found multiple declaration for the platform: macos"]) } else { @@ -252,7 +252,7 @@ class PackageDescription5_0LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in if case ManifestParseError.runtimeManifestErrors(let errors) = error { XCTAssertEqual(errors, ["supported platforms can't be empty"]) } else { @@ -274,7 +274,7 @@ class PackageDescription5_0LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in if case ManifestParseError.invalidManifestFormat(let message, _, _) = error { XCTAssertMatch(message, .contains("error: 'v11' is unavailable")) XCTAssertMatch(message, .contains("note: 'v11' was introduced in PackageDescription 5.3")) @@ -298,7 +298,7 @@ class PackageDescription5_0LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in if case ManifestParseError.invalidManifestFormat(let message, _, _) = error { XCTAssertMatch(message, .contains("error: 'v10_16' has been renamed to 'v11'")) XCTAssertMatch(message, .contains("note: 'v10_16' has been explicitly marked unavailable here")) @@ -310,7 +310,7 @@ class PackageDescription5_0LoadingTests: PackageDescriptionLoadingTests { } } - func testBuildSettings() throws { + func testBuildSettings() async throws { let content = """ import PackageDescription let package = Package( @@ -341,7 +341,7 @@ class PackageDescription5_0LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -361,8 +361,8 @@ class PackageDescription5_0LoadingTests: PackageDescriptionLoadingTests { XCTAssertEqual(settings[8], .init(tool: .linker, kind: .linkedFramework("CoreData"), condition: .init(platformNames: ["macos", "tvos"]))) } - func testSerializedDiagnostics() throws { - try testWithTemporaryDirectory { path in + func testSerializedDiagnostics() async throws { + try await testWithTemporaryDirectory { path in let fs = localFileSystem let manifestPath = path.appending(components: "pkg", "Package.swift") @@ -391,7 +391,7 @@ class PackageDescription5_0LoadingTests: PackageDescriptionLoadingTests { ) do { - _ = try loader.load( + _ = try await loader.load( manifestPath: manifestPath, packageKind: .fileSystem(manifestPath.parentDirectory), toolsVersion: .v5, @@ -426,7 +426,7 @@ class PackageDescription5_0LoadingTests: PackageDescriptionLoadingTests { """ ) - _ = try loader.load( + _ = try await loader.load( manifestPath: manifestPath, packageKind: .fileSystem(manifestPath.parentDirectory), toolsVersion: .v5, @@ -443,7 +443,7 @@ class PackageDescription5_0LoadingTests: PackageDescriptionLoadingTests { } } - func testInvalidBuildSettings() throws { + func testInvalidBuildSettings() async throws { do { let content = """ import PackageDescription @@ -461,7 +461,7 @@ class PackageDescription5_0LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in if case ManifestParseError.runtimeManifestErrors(let errors) = error { XCTAssertEqual(errors, ["the build setting 'headerSearchPath' contains invalid component(s): $(BYE) $(SRCROOT) $(HELLO)"]) } else { @@ -485,12 +485,12 @@ class PackageDescription5_0LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - _ = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + _ = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) } } - func testWindowsPlatform() throws { + func testWindowsPlatform() async throws { let content = """ import PackageDescription let package = Package( @@ -508,7 +508,7 @@ class PackageDescription5_0LoadingTests: PackageDescriptionLoadingTests { do { let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in if case ManifestParseError.invalidManifestFormat(let message, _, _) = error { XCTAssertMatch(message, .contains("is unavailable")) XCTAssertMatch(message, .contains("was introduced in PackageDescription 5.2")) @@ -520,7 +520,7 @@ class PackageDescription5_0LoadingTests: PackageDescriptionLoadingTests { do { let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, toolsVersion: .v5_2, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, toolsVersion: .v5_2, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -537,7 +537,7 @@ class PackageDescription5_0LoadingTests: PackageDescriptionLoadingTests { } } - func testPackageNameUnavailable() throws { + func testPackageNameUnavailable() async throws { let content = """ import PackageDescription let package = Package( @@ -555,7 +555,7 @@ class PackageDescription5_0LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in if case ManifestParseError.invalidManifestFormat(let message, _, _) = error { XCTAssertMatch(message, .contains("is unavailable")) XCTAssertMatch(message, .contains("was introduced in PackageDescription 5.2")) @@ -565,7 +565,7 @@ class PackageDescription5_0LoadingTests: PackageDescriptionLoadingTests { } } - func testManifestWithPrintStatements() throws { + func testManifestWithPrintStatements() async throws { let content = """ import PackageDescription print(String(repeating: "Hello manifest... ", count: 65536)) @@ -575,7 +575,7 @@ class PackageDescription5_0LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(validationDiagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -585,8 +585,8 @@ class PackageDescription5_0LoadingTests: PackageDescriptionLoadingTests { XCTAssertEqual(manifest.dependencies, []) } - func testManifestLoaderEnvironment() throws { - try testWithTemporaryDirectory { path in + func testManifestLoaderEnvironment() async throws { + try await testWithTemporaryDirectory { path in let fs = localFileSystem let packagePath = path.appending("pkg") @@ -620,7 +620,7 @@ class PackageDescription5_0LoadingTests: PackageDescriptionLoadingTests { cacheDir: nil) let observability = ObservabilitySystem.makeForTesting() - let manifest = try manifestLoader.load( + let manifest = try await manifestLoader.load( manifestPath: manifestPath, packageKind: .fileSystem(manifestPath.parentDirectory), toolsVersion: .v5, diff --git a/Tests/PackageLoadingTests/PD_5_11_LoadingTests.swift b/Tests/PackageLoadingTests/PD_5_11_LoadingTests.swift index 36b4c8a6f91..f394ac1bbda 100644 --- a/Tests/PackageLoadingTests/PD_5_11_LoadingTests.swift +++ b/Tests/PackageLoadingTests/PD_5_11_LoadingTests.swift @@ -21,37 +21,37 @@ class PackageDescription5_11LoadingTests: PackageDescriptionLoadingTests { .v5_11 } - func testPackageContextGitStatus() throws { + func testPackageContextGitStatus() async throws { let content = """ import PackageDescription let package = Package(name: "\\(Context.gitInformation?.hasUncommittedChanges == true)") """ - try loadRootManifestWithBasicGitRepository(manifestContent: content) { manifest, observability in + try await loadRootManifestWithBasicGitRepository(manifestContent: content) { manifest, observability in XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertEqual(manifest.displayName, "true") } } - func testPackageContextGitTag() throws { + func testPackageContextGitTag() async throws { let content = """ import PackageDescription let package = Package(name: "\\(Context.gitInformation?.currentTag ?? "")") """ - try loadRootManifestWithBasicGitRepository(manifestContent: content) { manifest, observability in + try await loadRootManifestWithBasicGitRepository(manifestContent: content) { manifest, observability in XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertEqual(manifest.displayName, "lunch") } } - func testPackageContextGitCommit() throws { + func testPackageContextGitCommit() async throws { let content = """ import PackageDescription let package = Package(name: "\\(Context.gitInformation?.currentCommit ?? "")") """ - try loadRootManifestWithBasicGitRepository(manifestContent: content) { manifest, observability in + try await loadRootManifestWithBasicGitRepository(manifestContent: content) { manifest, observability in XCTAssertNoDiagnostics(observability.diagnostics) let repo = GitRepository(path: manifest.path.parentDirectory) @@ -63,10 +63,10 @@ class PackageDescription5_11LoadingTests: PackageDescriptionLoadingTests { private func loadRootManifestWithBasicGitRepository( manifestContent: String, validator: (Manifest, TestingObservability) throws -> () - ) throws { + ) async throws { let observability = ObservabilitySystem.makeForTesting() - try testWithTemporaryDirectory { tmpdir in + try await testWithTemporaryDirectory { tmpdir in let manifestPath = tmpdir.appending(component: Manifest.filename) try localFileSystem.writeFileContents(manifestPath, string: manifestContent) try localFileSystem.writeFileContents(tmpdir.appending("best.txt"), string: "best") @@ -77,7 +77,7 @@ class PackageDescription5_11LoadingTests: PackageDescriptionLoadingTests { try repo.commit(message: "best") try repo.tag(name: "lunch") - let manifest = try manifestLoader.load( + let manifest = try await manifestLoader.load( manifestPath: manifestPath, packageKind: .root(tmpdir), toolsVersion: self.toolsVersion, diff --git a/Tests/PackageLoadingTests/PD_5_2_LoadingTests.swift b/Tests/PackageLoadingTests/PD_5_2_LoadingTests.swift index 436448b46c3..f9669df56b6 100644 --- a/Tests/PackageLoadingTests/PD_5_2_LoadingTests.swift +++ b/Tests/PackageLoadingTests/PD_5_2_LoadingTests.swift @@ -16,12 +16,12 @@ import PackageModel import SPMTestSupport import XCTest -class PackageDescription5_2LoadingTests: PackageDescriptionLoadingTests { +final class PackageDescription5_2LoadingTests: PackageDescriptionLoadingTests { override var toolsVersion: ToolsVersion { .v5_2 } - func testMissingTargetProductDependencyPackage() throws { + func testMissingTargetProductDependencyPackage() async throws { let content = """ import PackageDescription let package = Package( @@ -39,7 +39,7 @@ class PackageDescription5_2LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in if case ManifestParseError.invalidManifestFormat(let error, _, _) = error { XCTAssert(error.contains("error: \'product(name:package:)\' is unavailable: the 'package' argument is mandatory as of tools version 5.2")) } else { @@ -48,7 +48,7 @@ class PackageDescription5_2LoadingTests: PackageDescriptionLoadingTests { } } - func testDependencyNameForTargetDependencyResolution() throws { + func testDependencyNameForTargetDependencyResolution() async throws { let content = """ import PackageDescription let package = Package( @@ -85,7 +85,7 @@ class PackageDescription5_2LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -101,7 +101,7 @@ class PackageDescription5_2LoadingTests: PackageDescriptionLoadingTests { XCTAssertEqual(manifest.dependencies[8].nameForTargetDependencyResolutionOnly, "swift") } - func testTargetDependencyProductInvalidPackage() throws { + func testTargetDependencyProductInvalidPackage() async throws { do { let content = """ import PackageDescription @@ -124,7 +124,7 @@ class PackageDescription5_2LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (_, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (_, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) testDiagnostics(validationDiagnostics) { result in result.checkUnordered(diagnostic: "unknown package 'foo1' in dependencies of target 'Target1'; valid packages are: 'foo' (from 'http://scm.com/org/foo'), 'bar' (from 'http://scm.com/org/bar')", severity: .error) @@ -154,7 +154,7 @@ class PackageDescription5_2LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (_, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (_, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) testDiagnostics(validationDiagnostics) { result in result.checkUnordered(diagnostic: "unknown package 'foo1' in dependencies of target 'Target1'; valid packages are: 'Foo' (from 'http://scm.com/org/foo'), 'Bar' (from 'http://scm.com/org/bar')", severity: .error) @@ -185,7 +185,7 @@ class PackageDescription5_2LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (_, validationDiagnostics) = try loadAndValidateManifest(content, packageKind: .root(.root), observabilityScope: observability.topScope) + let (_, validationDiagnostics) = try await loadAndValidateManifest(content, packageKind: .root(.root), observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) testDiagnostics(validationDiagnostics) { result in result.check(diagnostic: "unknown package 'foo1' in dependencies of target 'Target1'; valid packages are: 'Foo' (from 'http://scm.com/org/foo1')", severity: .error) @@ -215,7 +215,7 @@ class PackageDescription5_2LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (_, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (_, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) testDiagnostics(validationDiagnostics) { result in let fooPkg: AbsolutePath = "/foo" @@ -247,7 +247,7 @@ class PackageDescription5_2LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (_, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (_, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) testDiagnostics(validationDiagnostics) { result in let foo1Pkg: AbsolutePath = "/foo1" @@ -258,7 +258,7 @@ class PackageDescription5_2LoadingTests: PackageDescriptionLoadingTests { } } - func testTargetDependencyReference() throws { + func testTargetDependencyReference() async throws { let content = """ import PackageDescription let package = Package( @@ -280,7 +280,7 @@ class PackageDescription5_2LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -294,7 +294,7 @@ class PackageDescription5_2LoadingTests: PackageDescriptionLoadingTests { XCTAssertEqual(manifest.packageDependency(referencedBy: targetBar.dependencies[0]), nil) } - func testResourcesUnavailable() throws { + func testResourcesUnavailable() async throws { let content = """ import PackageDescription let package = Package( @@ -312,7 +312,7 @@ class PackageDescription5_2LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in if case ManifestParseError.invalidManifestFormat(let error, _, _) = error { XCTAssertMatch(error, .contains("is unavailable")) XCTAssertMatch(error, .contains("was introduced in PackageDescription 5.3")) @@ -322,7 +322,7 @@ class PackageDescription5_2LoadingTests: PackageDescriptionLoadingTests { } } - func testBinaryTargetUnavailable() throws { + func testBinaryTargetUnavailable() async throws { do { let content = """ import PackageDescription @@ -338,7 +338,7 @@ class PackageDescription5_2LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in if case ManifestParseError.invalidManifestFormat(let error, _, _) = error { XCTAssertMatch(error, .contains("is unavailable")) XCTAssertMatch(error, .contains("was introduced in PackageDescription 5.3")) @@ -364,7 +364,7 @@ class PackageDescription5_2LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in if case ManifestParseError.invalidManifestFormat(let error, _, _) = error { XCTAssertMatch(error, .contains("is unavailable")) XCTAssertMatch(error, .contains("was introduced in PackageDescription 5.3")) @@ -375,7 +375,7 @@ class PackageDescription5_2LoadingTests: PackageDescriptionLoadingTests { } } - func testConditionalTargetDependenciesUnavailable() throws { + func testConditionalTargetDependenciesUnavailable() async throws { let content = """ import PackageDescription let package = Package( @@ -394,7 +394,7 @@ class PackageDescription5_2LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in if case ManifestParseError.invalidManifestFormat(let error, _, _) = error { XCTAssertMatch(error, .contains("is unavailable")) XCTAssertMatch(error, .contains("was introduced in PackageDescription 5.3")) @@ -404,7 +404,7 @@ class PackageDescription5_2LoadingTests: PackageDescriptionLoadingTests { } } - func testDefaultLocalizationUnavailable() throws { + func testDefaultLocalizationUnavailable() async throws { do { let content = """ import PackageDescription @@ -419,7 +419,7 @@ class PackageDescription5_2LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in if case ManifestParseError.invalidManifestFormat(let error, _, _) = error { XCTAssertMatch(error, .contains("is unavailable")) XCTAssertMatch(error, .contains("was introduced in PackageDescription 5.3")) @@ -430,7 +430,7 @@ class PackageDescription5_2LoadingTests: PackageDescriptionLoadingTests { } } - func testManifestLoadingIsSandboxed() throws { + func testManifestLoadingIsSandboxed() async throws { #if !os(macOS) // Sandboxing is only done on macOS today. try XCTSkipIf(true, "test is only supported on macOS") @@ -450,7 +450,7 @@ class PackageDescription5_2LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in if case ManifestParseError.invalidManifestFormat(let error, _, _) = error { XCTAssertTrue(error.contains("Operation not permitted"), "unexpected error message: \(error)") } else { diff --git a/Tests/PackageLoadingTests/PD_5_3_LoadingTests.swift b/Tests/PackageLoadingTests/PD_5_3_LoadingTests.swift index 276931c802a..7158e605a81 100644 --- a/Tests/PackageLoadingTests/PD_5_3_LoadingTests.swift +++ b/Tests/PackageLoadingTests/PD_5_3_LoadingTests.swift @@ -18,12 +18,12 @@ import XCTest import enum TSCBasic.PathValidationError -class PackageDescription5_3LoadingTests: PackageDescriptionLoadingTests { +final class PackageDescription5_3LoadingTests: PackageDescriptionLoadingTests { override var toolsVersion: ToolsVersion { .v5_3 } - func testResources() throws { + func testResources() async throws { let content = """ import PackageDescription let package = Package( @@ -49,7 +49,7 @@ class PackageDescription5_3LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -63,7 +63,7 @@ class PackageDescription5_3LoadingTests: PackageDescriptionLoadingTests { XCTAssertEqual(testResources[0], TargetDescription.Resource(rule: .process(localization: .none), path: "testfixture.txt")) } - func testBinaryTargetsTrivial() throws { + func testBinaryTargetsTrivial() async throws { let content = """ import PackageDescription let package = Package( @@ -88,7 +88,7 @@ class PackageDescription5_3LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -144,7 +144,7 @@ class PackageDescription5_3LoadingTests: PackageDescriptionLoadingTests { )) } - func testBinaryTargetsDisallowedProperties() throws { + func testBinaryTargetsDisallowedProperties() async throws { let content = """ import PackageDescription var fwBinaryTarget = Target.binaryTarget( @@ -157,12 +157,12 @@ class PackageDescription5_3LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in XCTAssertEqual(error.localizedDescription, "target 'Foo' contains a value for disallowed property 'settings'") } } - func testBinaryTargetsValidation() throws { + func testBinaryTargetsValidation() async throws { do { let content = """ import PackageDescription @@ -178,7 +178,7 @@ class PackageDescription5_3LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (_, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (_, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) testDiagnostics(validationDiagnostics) { result in result.check(diagnostic: "invalid type for binary product 'FooLibrary'; products referencing only binary targets must be executable or automatic library products", severity: .error) @@ -201,7 +201,7 @@ class PackageDescription5_3LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (_, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (_, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) } @@ -221,7 +221,7 @@ class PackageDescription5_3LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (_, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (_, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) testDiagnostics(validationDiagnostics) { result in result.check(diagnostic: "invalid local path ' ' for binary target 'Foo', path expected to be relative to package root.", severity: .error) @@ -243,7 +243,7 @@ class PackageDescription5_3LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (_, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (_, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) testDiagnostics(validationDiagnostics) { result in result.check(diagnostic: "invalid URL scheme for binary target 'Foo'; valid schemes are: 'https'", severity: .error) @@ -265,7 +265,7 @@ class PackageDescription5_3LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (_, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (_, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) testDiagnostics(validationDiagnostics) { result in result.check(diagnostic: "unsupported extension for binary target 'Foo'; valid extensions are: 'zip', 'xcframework', 'artifactbundle'", severity: .error) @@ -290,7 +290,7 @@ class PackageDescription5_3LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (_, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (_, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) testDiagnostics(validationDiagnostics) { result in result.check(diagnostic: "unsupported extension for binary target 'Foo'; valid extensions are: 'zip', 'artifactbundleindex'", severity: .error) @@ -312,7 +312,7 @@ class PackageDescription5_3LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (_, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (_, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) testDiagnostics(validationDiagnostics) { result in result.check(diagnostic: "unsupported extension for binary target 'Foo'; valid extensions are: 'zip', 'xcframework', 'artifactbundle'", severity: .error) @@ -337,7 +337,7 @@ class PackageDescription5_3LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (_, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (_, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) testDiagnostics(validationDiagnostics) { result in result.check(diagnostic: "unsupported extension for binary target 'Foo'; valid extensions are: 'zip', 'artifactbundleindex'", severity: .error) @@ -362,7 +362,7 @@ class PackageDescription5_3LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (_, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (_, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) testDiagnostics(validationDiagnostics) { result in result.check(diagnostic: "invalid URL scheme for binary target 'Foo'; valid schemes are: 'https'", severity: .error) @@ -387,7 +387,7 @@ class PackageDescription5_3LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (_, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (_, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) testDiagnostics(validationDiagnostics) { result in result.check(diagnostic: "invalid URL ' ' for binary target 'Foo'", severity: .error) @@ -411,7 +411,7 @@ class PackageDescription5_3LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (_, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (_, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) testDiagnostics(validationDiagnostics) { result in result.check(diagnostic: "invalid local path '/tmp/foo/bar' for binary target 'Foo', path expected to be relative to package root.", severity: .error) @@ -419,7 +419,7 @@ class PackageDescription5_3LoadingTests: PackageDescriptionLoadingTests { } } - func testConditionalTargetDependencies() throws { + func testConditionalTargetDependencies() async throws { let content = """ import PackageDescription let package = Package( @@ -441,7 +441,7 @@ class PackageDescription5_3LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -452,7 +452,7 @@ class PackageDescription5_3LoadingTests: PackageDescriptionLoadingTests { XCTAssertEqual(dependencies[3], .byName(name: "Bar", condition: .init(platformNames: ["watchos", "ios"]))) } - func testDefaultLocalization() throws { + func testDefaultLocalization() async throws { let content = """ import PackageDescription let package = Package( @@ -465,13 +465,13 @@ class PackageDescription5_3LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) XCTAssertEqual(manifest.defaultLocalization, "fr") } - func testTargetPathsValidation() throws { + func testTargetPathsValidation() async throws { let manifestItemToDiagnosticMap = [ "sources: [\"/foo.swift\"]": "invalid relative path '/foo.swift", "resources: [.copy(\"/foo.txt\")]": "invalid relative path '/foo.txt'", @@ -493,7 +493,7 @@ class PackageDescription5_3LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in if let error = error as? PathValidationError { XCTAssertMatch(error.description, .contains(expectedDiag)) } else { @@ -503,7 +503,7 @@ class PackageDescription5_3LoadingTests: PackageDescriptionLoadingTests { } } - func testNonZeroExitStatusDoesNotAssert() throws { + func testNonZeroExitStatusDoesNotAssert() async throws { let content = """ #if canImport(Glibc) import Glibc @@ -521,12 +521,12 @@ class PackageDescription5_3LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in XCTAssertNotNil(error as? ManifestParseError) } } - func testManifestLoadingIsSandboxed() throws { + func testManifestLoadingIsSandboxed() async throws { #if !os(macOS) // Sandboxing is only done on macOS today. try XCTSkipIf(true, "test is only supported on macOS") @@ -546,7 +546,7 @@ class PackageDescription5_3LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in if case ManifestParseError.invalidManifestFormat(let error, _, _) = error { XCTAssertTrue(error.contains("Operation not permitted"), "unexpected error message: \(error)") } else { diff --git a/Tests/PackageLoadingTests/PD_5_4_LoadingTests.swift b/Tests/PackageLoadingTests/PD_5_4_LoadingTests.swift index 211a889583f..2e39b3db248 100644 --- a/Tests/PackageLoadingTests/PD_5_4_LoadingTests.swift +++ b/Tests/PackageLoadingTests/PD_5_4_LoadingTests.swift @@ -21,7 +21,7 @@ class PackageDescription5_4LoadingTests: PackageDescriptionLoadingTests { .v5_4 } - func testExecutableTargets() throws { + func testExecutableTargets() async throws { let content = """ import PackageDescription let package = Package( @@ -35,14 +35,14 @@ class PackageDescription5_4LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) XCTAssertEqual(manifest.targets[0].type, .executable) } - func testPluginsAreUnavailable() throws { + func testPluginsAreUnavailable() async throws { let content = """ import PackageDescription let package = Package( @@ -57,7 +57,7 @@ class PackageDescription5_4LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in if case ManifestParseError.invalidManifestFormat(let message, _, _) = error { XCTAssertMatch(message, .contains("is unavailable")) XCTAssertMatch(message, .contains("was introduced in PackageDescription 5.5")) diff --git a/Tests/PackageLoadingTests/PD_5_5_LoadingTests.swift b/Tests/PackageLoadingTests/PD_5_5_LoadingTests.swift index 5096d829c1f..d94c0600ed8 100644 --- a/Tests/PackageLoadingTests/PD_5_5_LoadingTests.swift +++ b/Tests/PackageLoadingTests/PD_5_5_LoadingTests.swift @@ -22,7 +22,7 @@ class PackageDescription5_5LoadingTests: PackageDescriptionLoadingTests { .v5_5 } - func testPackageDependencies() throws { + func testPackageDependencies() async throws { let content = """ import PackageDescription let package = Package( @@ -35,7 +35,7 @@ class PackageDescription5_5LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -44,7 +44,7 @@ class PackageDescription5_5LoadingTests: PackageDescriptionLoadingTests { XCTAssertEqual(deps["foo7"], .localSourceControl(path: "/foo7", requirement: .revision("58e9de4e7b79e67c72a46e164158e3542e570ab6"))) } - func testPlatforms() throws { + func testPlatforms() async throws { let content = """ import PackageDescription let package = Package( @@ -58,7 +58,7 @@ class PackageDescription5_5LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) diff --git a/Tests/PackageLoadingTests/PD_5_6_LoadingTests.swift b/Tests/PackageLoadingTests/PD_5_6_LoadingTests.swift index 77a67485b3e..8abb54efe97 100644 --- a/Tests/PackageLoadingTests/PD_5_6_LoadingTests.swift +++ b/Tests/PackageLoadingTests/PD_5_6_LoadingTests.swift @@ -16,12 +16,12 @@ import PackageModel import SPMTestSupport import XCTest -class PackageDescription5_6LoadingTests: PackageDescriptionLoadingTests { +final class PackageDescription5_6LoadingTests: PackageDescriptionLoadingTests { override var toolsVersion: ToolsVersion { .v5_6 } - func testSourceControlDependencies() throws { + func testSourceControlDependencies() async throws { let content = """ import PackageDescription let package = Package( @@ -53,7 +53,7 @@ class PackageDescription5_6LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertFalse(observability.diagnostics.hasErrors) XCTAssertNoDiagnostics(validationDiagnostics) @@ -75,7 +75,7 @@ class PackageDescription5_6LoadingTests: PackageDescriptionLoadingTests { XCTAssertEqual(deps["quuz3"], .remoteSourceControl(identity: .plain("quuz3"), url: "http://localhost/quuz3", requirement: .revision("abcdefg"))) } - func testBuildToolPluginTarget() throws { + func testBuildToolPluginTarget() async throws { let content = """ import PackageDescription let package = Package( @@ -90,7 +90,7 @@ class PackageDescription5_6LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -98,7 +98,7 @@ class PackageDescription5_6LoadingTests: PackageDescriptionLoadingTests { XCTAssertEqual(manifest.targets[0].pluginCapability, .buildTool) } - func testPluginTargetCustomization() throws { + func testPluginTargetCustomization() async throws { let content = """ import PackageDescription let package = Package( @@ -116,7 +116,7 @@ class PackageDescription5_6LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -127,7 +127,7 @@ class PackageDescription5_6LoadingTests: PackageDescriptionLoadingTests { XCTAssertEqual(manifest.targets[0].sources, ["CountMeIn.swift"]) } - func testCustomPlatforms() throws { + func testCustomPlatforms() async throws { // One custom platform. do { let content = """ @@ -141,7 +141,7 @@ class PackageDescription5_6LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -164,7 +164,7 @@ class PackageDescription5_6LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -188,23 +188,23 @@ class PackageDescription5_6LoadingTests: PackageDescriptionLoadingTests { let observability = ObservabilitySystem.makeForTesting() do { - _ = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + _ = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTFail("manifest loading unexpectedly did not throw an error") } catch ManifestParseError.runtimeManifestErrors(let errors) { XCTAssertEqual(errors, ["invalid custom platform version xx; xx should be a positive integer"]) } } } - + /// Tests use of Context.current.packageDirectory - func testPackageContextName() throws { + func testPackageContextName() async throws { let content = """ import PackageDescription let package = Package(name: Context.packageDirectory) """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -215,7 +215,7 @@ class PackageDescription5_6LoadingTests: PackageDescriptionLoadingTests { } /// Tests access to the package's directory contents. - func testPackageContextDirectory() throws { + func testPackageContextDirectory() async throws { #if os(Windows) throw XCTSkip("Skipping since this tests does not fully work without the VFS overlay which is currently disabled on Windows") #endif @@ -223,15 +223,15 @@ class PackageDescription5_6LoadingTests: PackageDescriptionLoadingTests { let content = """ import PackageDescription import Foundation - + let fileManager = FileManager.default let contents = (try? fileManager.contentsOfDirectory(atPath: Context.packageDirectory)) ?? [] - + let package = Package(name: contents.joined(separator: ",")) """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) // FIXME: temporary filter a diagnostic that shows up on macOS 14.0 XCTAssertNoDiagnostics(observability.diagnostics.filter { !$0.message.contains("coreservicesd") }) XCTAssertNoDiagnostics(validationDiagnostics) @@ -242,7 +242,7 @@ class PackageDescription5_6LoadingTests: PackageDescriptionLoadingTests { XCTAssertEqual(files, expectedFiles) } - func testCommandPluginTarget() throws { + func testCommandPluginTarget() async throws { let content = """ import PackageDescription let package = Package( @@ -260,7 +260,7 @@ class PackageDescription5_6LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) diff --git a/Tests/PackageLoadingTests/PD_5_7_LoadingTests.swift b/Tests/PackageLoadingTests/PD_5_7_LoadingTests.swift index a21d9ff5ed4..981eca47913 100644 --- a/Tests/PackageLoadingTests/PD_5_7_LoadingTests.swift +++ b/Tests/PackageLoadingTests/PD_5_7_LoadingTests.swift @@ -16,12 +16,12 @@ import PackageModel import SPMTestSupport import XCTest -class PackageDescription5_7LoadingTests: PackageDescriptionLoadingTests { +final class PackageDescription5_7LoadingTests: PackageDescriptionLoadingTests { override var toolsVersion: ToolsVersion { .v5_7 } - func testImplicitFoundationImportWorks() throws { + func testImplicitFoundationImportWorks() async throws { let content = """ import PackageDescription @@ -31,13 +31,13 @@ class PackageDescription5_7LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) XCTAssertEqual(manifest.displayName, "MyPackage") } - func testRegistryDependencies() throws { + func testRegistryDependencies() async throws { let content = """ import PackageDescription let package = Package( @@ -53,7 +53,7 @@ class PackageDescription5_7LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -65,7 +65,7 @@ class PackageDescription5_7LoadingTests: PackageDescriptionLoadingTests { XCTAssertEqual(deps["x.quux"], .registry(identity: "x.quux", requirement: .range("1.1.1" ..< "3.0.0"))) } - func testConditionalTargetDependencies() throws { + func testConditionalTargetDependencies() async throws { let content = """ import PackageDescription let package = Package( @@ -83,7 +83,7 @@ class PackageDescription5_7LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -92,7 +92,7 @@ class PackageDescription5_7LoadingTests: PackageDescriptionLoadingTests { XCTAssertEqual(dependencies[1], .target(name: "Baz", condition: .init(platformNames: ["linux"], config: .none))) } - func testConditionalTargetDependenciesDeprecation() throws { + func testConditionalTargetDependenciesDeprecation() async throws { let content = """ import PackageDescription let package = Package( @@ -108,7 +108,7 @@ class PackageDescription5_7LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in if case ManifestParseError.invalidManifestFormat(let error, _, _) = error { XCTAssertMatch(error, .contains("when(platforms:)' was obsoleted")) } else { @@ -117,7 +117,7 @@ class PackageDescription5_7LoadingTests: PackageDescriptionLoadingTests { } } - func testTargetDeprecatedDependencyCase() throws { + func testTargetDeprecatedDependencyCase() async throws { let content = """ import PackageDescription let package = Package( @@ -135,7 +135,7 @@ class PackageDescription5_7LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope)) { error in + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, observabilityScope: observability.topScope)) { error in if case ManifestParseError.invalidManifestFormat(let message, _, _) = error { XCTAssertMatch(message, .contains("error: 'productItem(name:package:condition:)' is unavailable: use .product(name:package:condition) instead.")) } else { @@ -144,7 +144,7 @@ class PackageDescription5_7LoadingTests: PackageDescriptionLoadingTests { } } - func testPlatforms() throws { + func testPlatforms() async throws { let content = """ import PackageDescription let package = Package( @@ -158,7 +158,7 @@ class PackageDescription5_7LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -172,7 +172,7 @@ class PackageDescription5_7LoadingTests: PackageDescriptionLoadingTests { ]) } - func testImportRestrictions() throws { + func testImportRestrictions() async throws { let content = """ import PackageDescription import BestModule @@ -181,7 +181,7 @@ class PackageDescription5_7LoadingTests: PackageDescriptionLoadingTests { let observability = ObservabilitySystem.makeForTesting() let manifestLoader = ManifestLoader(toolchain: try UserToolchain.default, importRestrictions: (.v5_7, [])) - XCTAssertThrowsError(try loadAndValidateManifest(content, customManifestLoader: manifestLoader, observabilityScope: observability.topScope)) { error in + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, customManifestLoader: manifestLoader, observabilityScope: observability.topScope)) { error in if case ManifestParseError.importsRestrictedModules(let modules) = error { XCTAssertEqual(modules.sorted(), ["BestModule", "Foundation"]) } else { @@ -190,7 +190,7 @@ class PackageDescription5_7LoadingTests: PackageDescriptionLoadingTests { } } - func testTargetDependencyProductInvalidPackage() throws { + func testTargetDependencyProductInvalidPackage() async throws { do { let content = """ import PackageDescription @@ -213,7 +213,7 @@ class PackageDescription5_7LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (_, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (_, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) testDiagnostics(validationDiagnostics) { result in result.checkUnordered(diagnostic: "unknown package 'org.baz' in dependencies of target 'Target1'; valid packages are: 'org.foo', 'org.bar'", severity: .error) diff --git a/Tests/PackageLoadingTests/PD_5_9_LoadingTests.swift b/Tests/PackageLoadingTests/PD_5_9_LoadingTests.swift index 5890021a1f1..88f712c2ceb 100644 --- a/Tests/PackageLoadingTests/PD_5_9_LoadingTests.swift +++ b/Tests/PackageLoadingTests/PD_5_9_LoadingTests.swift @@ -16,12 +16,12 @@ import PackageModel import SPMTestSupport import XCTest -class PackageDescription5_9LoadingTests: PackageDescriptionLoadingTests { +final class PackageDescription5_9LoadingTests: PackageDescriptionLoadingTests { override var toolsVersion: ToolsVersion { .v5_9 } - func testPlatforms() throws { + func testPlatforms() async throws { let content = """ import PackageDescription let package = Package( @@ -35,7 +35,7 @@ class PackageDescription5_9LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (manifest, validationDiagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (manifest, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertNoDiagnostics(validationDiagnostics) @@ -50,7 +50,7 @@ class PackageDescription5_9LoadingTests: PackageDescriptionLoadingTests { ]) } - func testMacroTargets() throws { + func testMacroTargets() async throws { let content = """ import CompilerPluginSupport import PackageDescription @@ -63,7 +63,7 @@ class PackageDescription5_9LoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - let (_, diagnostics) = try loadAndValidateManifest(content, observabilityScope: observability.topScope) + let (_, diagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) XCTAssertEqual(diagnostics.count, 0, "unexpected diagnostics: \(diagnostics)") } } diff --git a/Tests/PackageLoadingTests/PD_Next_LoadingTests.swift b/Tests/PackageLoadingTests/PD_Next_LoadingTests.swift index 4c0d6da3644..f922e03e332 100644 --- a/Tests/PackageLoadingTests/PD_Next_LoadingTests.swift +++ b/Tests/PackageLoadingTests/PD_Next_LoadingTests.swift @@ -21,7 +21,7 @@ class PackageDescriptionNextLoadingTests: PackageDescriptionLoadingTests { .vNext } - func testImplicitFoundationImportFails() throws { + func testImplicitFoundationImportFails() async throws { let content = """ import PackageDescription @@ -31,7 +31,7 @@ class PackageDescriptionNextLoadingTests: PackageDescriptionLoadingTests { """ let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { + await XCTAssertAsyncThrowsError(try await loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { if case ManifestParseError.invalidManifestFormat(let error, _, _) = $0 { XCTAssertMatch(error, .contains("cannot find 'FileManager' in scope")) } else { From fc1b37ab2a711d7bdf7cc09902eade266bae8f92 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 23 Feb 2024 16:22:19 +0000 Subject: [PATCH 021/159] Don't link libc++ or libstd++ for Embedded Swift (#7357) These libraries are not usually available for embedded platforms. A workaround of passing this flag via `unsafeFlags` in `Package.swift` still remains after this change. --- .../Build/BuildPlan/BuildPlan+Product.swift | 37 ++++++++++------ Sources/PackageModel/Target/Target.swift | 16 +++++++ .../SPMTestSupport/MockPackageGraphs.swift | 43 +++++++++++++++++++ .../CrossCompilationBuildPlanTests.swift | 38 ++++++++++++++-- 4 files changed, 117 insertions(+), 17 deletions(-) diff --git a/Sources/Build/BuildPlan/BuildPlan+Product.swift b/Sources/Build/BuildPlan/BuildPlan+Product.swift index d14ec2418ad..e04daac0130 100644 --- a/Sources/Build/BuildPlan/BuildPlan+Product.swift +++ b/Sources/Build/BuildPlan/BuildPlan+Product.swift @@ -17,7 +17,10 @@ import struct PackageGraph.ResolvedProduct import struct PackageGraph.ResolvedTarget import class PackageModel.BinaryTarget import class PackageModel.ClangTarget + +@_spi(SwiftPMInternal) import class PackageModel.Target + import class PackageModel.SwiftTarget import class PackageModel.SystemLibraryTarget import struct SPMBuildCore.BuildParameters @@ -28,7 +31,10 @@ extension BuildPlan { /// Plan a product. func plan(buildProduct: ProductBuildDescription) throws { // Compute the product's dependency. - let dependencies = try computeDependencies(of: buildProduct.product, buildParameters: buildProduct.buildParameters) + let dependencies = try computeDependencies( + of: buildProduct.product, + buildParameters: buildProduct.buildParameters + ) // Add flags for system targets. for systemModule in dependencies.systemModules { @@ -50,19 +56,24 @@ extension BuildPlan { } } - // Link C++ if needed. - // Note: This will come from build settings in future. - for target in dependencies.staticTargets { - if case let target as ClangTarget = target.underlying, target.isCXX { - let triple = buildProduct.buildParameters.triple - if triple.isDarwin() { - buildProduct.additionalFlags += ["-lc++"] - } else if triple.isWindows() { - // Don't link any C++ library. - } else { - buildProduct.additionalFlags += ["-lstdc++"] + // Don't link libc++ or libstd++ when building for Embedded Swift. + // Users can still link it manually for embedded platforms when needed, + // by providing `-Xlinker -lc++` options via CLI or `Package.swift`. + if !buildProduct.product.targets.contains(where: \.underlying.isEmbeddedSwiftTarget) { + // Link C++ if needed. + // Note: This will come from build settings in future. + for target in dependencies.staticTargets { + if case let target as ClangTarget = target.underlying, target.isCXX { + let triple = buildProduct.buildParameters.triple + if triple.isDarwin() { + buildProduct.additionalFlags += ["-lc++"] + } else if triple.isWindows() { + // Don't link any C++ library. + } else { + buildProduct.additionalFlags += ["-lstdc++"] + } + break } - break } } diff --git a/Sources/PackageModel/Target/Target.swift b/Sources/PackageModel/Target/Target.swift index 4754fe185e5..c49b93ba142 100644 --- a/Sources/PackageModel/Target/Target.swift +++ b/Sources/PackageModel/Target/Target.swift @@ -339,6 +339,15 @@ public class Target: PolymorphicCodableProtocol { self.pluginUsages = [] self.usesUnsafeFlags = try container.decode(Bool.self, forKey: .usesUnsafeFlags) } + + @_spi(SwiftPMInternal) + public var isEmbeddedSwiftTarget: Bool { + for case .enableExperimentalFeature("Embedded") in self.buildSettingsDescription.swiftSettings.map(\.kind) { + return true + } + + return false + } } extension Target: Hashable { @@ -371,3 +380,10 @@ public extension Sequence where Iterator.Element == Target { } } } + +extension [TargetBuildSettingDescription.Setting] { + @_spi(SwiftPMInternal) + public var swiftSettings: Self { + self.filter { $0.tool == .swift } + } +} diff --git a/Sources/SPMTestSupport/MockPackageGraphs.swift b/Sources/SPMTestSupport/MockPackageGraphs.swift index a068e54fb64..1d62598796d 100644 --- a/Sources/SPMTestSupport/MockPackageGraphs.swift +++ b/Sources/SPMTestSupport/MockPackageGraphs.swift @@ -156,3 +156,46 @@ public func trivialPackageGraph(pkgRootPath: AbsolutePath) throws -> MockPackage return (graph, fs, observability.topScope) } + +@_spi(SwiftPMInternal) +public func embeddedCxxInteropPackageGraph(pkgRootPath: AbsolutePath) throws -> MockPackageGraph { + let fs = InMemoryFileSystem( + emptyFiles: + "/Pkg/Sources/app/main.swift", + "/Pkg/Sources/lib/lib.cpp", + "/Pkg/Sources/lib/include/lib.h", + "/Pkg/Tests/test/TestCase.swift" + ) + + let observability = ObservabilitySystem.makeForTesting() + let graph = try loadPackageGraph( + fileSystem: fs, + manifests: [ + Manifest.createRootManifest( + displayName: "Pkg", + path: "/Pkg", + targets: [ + TargetDescription( + name: "app", + dependencies: ["lib"], + settings: [.init(tool: .swift, kind: .enableExperimentalFeature("Embedded"))] + ), + TargetDescription( + name: "lib", + dependencies: [], + settings: [.init(tool: .swift, kind: .interoperabilityMode(.Cxx))] + ), + TargetDescription( + name: "test", + dependencies: ["lib"], + type: .test + ), + ] + ), + ], + observabilityScope: observability.topScope + ) + XCTAssertNoDiagnostics(observability.diagnostics) + + return (graph, fs, observability.topScope) +} diff --git a/Tests/BuildTests/CrossCompilationBuildPlanTests.swift b/Tests/BuildTests/CrossCompilationBuildPlanTests.swift index be15e15af0e..dcb996bed3c 100644 --- a/Tests/BuildTests/CrossCompilationBuildPlanTests.swift +++ b/Tests/BuildTests/CrossCompilationBuildPlanTests.swift @@ -20,6 +20,9 @@ import class PackageModel.Manifest import struct PackageModel.TargetDescription import func SPMTestSupport.loadPackageGraph +@_spi(SwiftPMInternal) +import func SPMTestSupport.embeddedCxxInteropPackageGraph + @_spi(SwiftPMInternal) import func SPMTestSupport.macrosPackageGraph @@ -37,13 +40,12 @@ import XCTest final class CrossCompilationBuildPlanTests: XCTestCase { func testEmbeddedWasmTarget() throws { - let pkgPath = AbsolutePath("/Pkg") - let (graph, fs, observabilityScope) = try trivialPackageGraph(pkgRootPath: pkgPath) + var (graph, fs, observabilityScope) = try trivialPackageGraph(pkgRootPath: "/Pkg") let triple = try Triple("wasm32-unknown-none-wasm") var parameters = mockBuildParameters(targetTriple: triple) parameters.linkingParameters.shouldLinkStaticSwiftStdlib = true - let result = try BuildPlanResult(plan: BuildPlan( + var result = try BuildPlanResult(plan: BuildPlan( buildParameters: parameters, graph: graph, fileSystem: fs, @@ -55,7 +57,34 @@ final class CrossCompilationBuildPlanTests: XCTestCase { result.checkTargetsCount(5) let buildPath = result.plan.productsBuildPath - let appBuildDescription = try result.buildProduct(for: "app") + var appBuildDescription = try result.buildProduct(for: "app") + XCTAssertEqual( + try appBuildDescription.linkArguments(), + [ + result.plan.destinationBuildParameters.toolchain.swiftCompilerPath.pathString, + "-L", buildPath.pathString, + "-o", buildPath.appending(components: "app.wasm").pathString, + "-module-name", "app", "-static-stdlib", "-emit-executable", + "@\(buildPath.appending(components: "app.product", "Objects.LinkFileList"))", + "-target", triple.tripleString, + "-g", + ] + ) + + (graph, fs, observabilityScope) = try embeddedCxxInteropPackageGraph(pkgRootPath: "/Pkg") + + result = try BuildPlanResult(plan: BuildPlan( + buildParameters: parameters, + graph: graph, + fileSystem: fs, + observabilityScope: observabilityScope + )) + result.checkProductsCount(2) + // There are two additional targets on non-Apple platforms, for test discovery and + // test entry point + result.checkTargetsCount(5) + + appBuildDescription = try result.buildProduct(for: "app") XCTAssertEqual( try appBuildDescription.linkArguments(), [ @@ -64,6 +93,7 @@ final class CrossCompilationBuildPlanTests: XCTestCase { "-o", buildPath.appending(components: "app.wasm").pathString, "-module-name", "app", "-static-stdlib", "-emit-executable", "@\(buildPath.appending(components: "app.product", "Objects.LinkFileList"))", + "-enable-experimental-feature", "Embedded", "-target", triple.tripleString, "-g", ] From d7878b04971bda07ee11459e8c9cae62e77f697b Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 23 Feb 2024 18:05:04 +0000 Subject: [PATCH 022/159] Replace 5.11 version with 6.0 in the code base (#7361) This is a follow up to a corresponding toolchain PR https://github.com/apple/swift/pull/71707. --- .../Package.swift | 2 +- .../Plugins/DependentPlugins/Package.swift | 2 +- .../Package.swift | 2 +- Sources/Basics/SwiftVersion.swift | 2 +- .../LLBuildManifestBuilder.swift | 4 +- Sources/Commands/PackageTools/Init.swift | 2 +- Sources/Commands/Utilities/APIDigester.swift | 2 +- Sources/CoreCommands/Options.swift | 2 +- Sources/PackageDescription/Context.swift | 4 +- .../PackageLoading/TargetSourcesBuilder.swift | 2 +- .../PackageLoading/ToolsVersionParser.swift | 18 ++++---- Sources/PackageModel/ToolsVersion.swift | 2 +- Sources/PackagePlugin/Command.swift | 8 ++-- Sources/PackagePlugin/Context.swift | 12 ++--- .../PackagePlugin/PackageManagerProxy.swift | 12 ++--- Sources/PackagePlugin/PackageModel.swift | 44 +++++++++---------- Sources/PackagePlugin/Path.swift | 26 +++++------ .../PackageRegistryTool+Publish.swift | 2 +- .../Plugins/PluginInvocation.swift | 4 +- Sources/SPMTestSupport/misc.swift | 2 +- Sources/Workspace/Diagnostics.swift | 2 +- Sources/Workspace/Workspace.swift | 2 +- Sources/swift-bootstrap/main.swift | 2 +- Tests/FunctionalTests/PluginTests.swift | 2 +- Tests/FunctionalTests/ResourcesTests.swift | 2 +- Tests/FunctionalTests/ToolsVersionTests.swift | 5 ++- .../DependencyResolverPerfTests.swift | 2 +- .../Inputs/kitura.json | 2 +- Tests/PackageGraphTests/PubgrubTests.swift | 2 +- .../TopologicalSortTests.swift | 2 +- ...gTests.swift => PD_6_0_LoadingTests.swift} | 4 +- .../PackageBuilderTests.swift | 29 ++++++++++-- .../ToolsVersionParserTests.swift | 10 ++--- 33 files changed, 121 insertions(+), 99 deletions(-) rename Tests/PackageLoadingTests/{PD_5_11_LoadingTests.swift => PD_6_0_LoadingTests.swift} (97%) diff --git a/Fixtures/Miscellaneous/DoNotFilterLinkerDiagnostics/Package.swift b/Fixtures/Miscellaneous/DoNotFilterLinkerDiagnostics/Package.swift index 326e3041c52..45efb831fa1 100644 --- a/Fixtures/Miscellaneous/DoNotFilterLinkerDiagnostics/Package.swift +++ b/Fixtures/Miscellaneous/DoNotFilterLinkerDiagnostics/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 5.11 +// swift-tools-version: 6.0 import PackageDescription diff --git a/Fixtures/Miscellaneous/Plugins/DependentPlugins/Package.swift b/Fixtures/Miscellaneous/Plugins/DependentPlugins/Package.swift index ca0411b5e2e..9d77aa3bd15 100644 --- a/Fixtures/Miscellaneous/Plugins/DependentPlugins/Package.swift +++ b/Fixtures/Miscellaneous/Plugins/DependentPlugins/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 5.11 +// swift-tools-version: 6.0 import PackageDescription diff --git a/Fixtures/Miscellaneous/Plugins/MySourceGenPluginUsingURLBasedAPI/Package.swift b/Fixtures/Miscellaneous/Plugins/MySourceGenPluginUsingURLBasedAPI/Package.swift index a16a412ff15..35a06c404bc 100644 --- a/Fixtures/Miscellaneous/Plugins/MySourceGenPluginUsingURLBasedAPI/Package.swift +++ b/Fixtures/Miscellaneous/Plugins/MySourceGenPluginUsingURLBasedAPI/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 5.11 +// swift-tools-version: 6.0 import PackageDescription let package = Package( diff --git a/Sources/Basics/SwiftVersion.swift b/Sources/Basics/SwiftVersion.swift index 0d479c4cdb5..d3967783532 100644 --- a/Sources/Basics/SwiftVersion.swift +++ b/Sources/Basics/SwiftVersion.swift @@ -58,7 +58,7 @@ public struct SwiftVersion { extension SwiftVersion { /// The current version of the package manager. public static let current = SwiftVersion( - version: (5, 11, 0), + version: (6, 0, 0), isDevelopment: true, buildIdentifier: getBuildIdentifier() ) diff --git a/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift b/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift index 4d9b6d62787..8ff1455360a 100644 --- a/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift +++ b/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift @@ -220,12 +220,12 @@ extension LLBuildManifestBuilder { } let additionalOutputs: [Node] if command.outputFiles.isEmpty { - if target.toolsVersion >= .v5_11 { + if target.toolsVersion >= .v6_0 { additionalOutputs = [.virtual("\(target.target.c99name)-\(command.configuration.displayName ?? "\(pluginNumber)")")] phonyOutputs += additionalOutputs } else { additionalOutputs = [] - observabilityScope.emit(warning: "Build tool command '\(displayName)' (applied to target '\(target.target.name)') does not declare any output files and therefore will not run. You may want to consider updating the given package to tools-version 5.11 (or higher) which would run such a build tool command even without declared outputs.") + observabilityScope.emit(warning: "Build tool command '\(displayName)' (applied to target '\(target.target.name)') does not declare any output files and therefore will not run. You may want to consider updating the given package to tools-version 6.0 (or higher) which would run such a build tool command even without declared outputs.") } pluginNumber += 1 } else { diff --git a/Sources/Commands/PackageTools/Init.swift b/Sources/Commands/PackageTools/Init.swift index 51ce5afd2c8..444ddce142a 100644 --- a/Sources/Commands/PackageTools/Init.swift +++ b/Sources/Commands/PackageTools/Init.swift @@ -83,7 +83,7 @@ extension SwiftPackageTool { } } -#if swift(<5.11) +#if swift(<6.0) extension InitPackage.PackageType: ExpressibleByArgument {} #else extension InitPackage.PackageType: @retroactive ExpressibleByArgument {} diff --git a/Sources/Commands/Utilities/APIDigester.swift b/Sources/Commands/Utilities/APIDigester.swift index 28f47e47406..b69465eb483 100644 --- a/Sources/Commands/Utilities/APIDigester.swift +++ b/Sources/Commands/Utilities/APIDigester.swift @@ -328,7 +328,7 @@ extension SerializedDiagnostics.SourceLocation { } } -#if swift(<5.11) +#if swift(<6.0) extension SerializedDiagnostics.SourceLocation: DiagnosticLocation {} #else extension SerializedDiagnostics.SourceLocation: @retroactive DiagnosticLocation {} diff --git a/Sources/CoreCommands/Options.swift b/Sources/CoreCommands/Options.swift index e04e4e7ecfe..006372edfff 100644 --- a/Sources/CoreCommands/Options.swift +++ b/Sources/CoreCommands/Options.swift @@ -606,7 +606,7 @@ extension URL { } } -#if swift(<5.11) +#if swift(<6.0) extension BuildConfiguration: ExpressibleByArgument {} extension AbsolutePath: ExpressibleByArgument {} extension WorkspaceConfiguration.CheckingMode: ExpressibleByArgument {} diff --git a/Sources/PackageDescription/Context.swift b/Sources/PackageDescription/Context.swift index a931d880628..eb192f65f19 100644 --- a/Sources/PackageDescription/Context.swift +++ b/Sources/PackageDescription/Context.swift @@ -24,7 +24,7 @@ public struct Context { } /// Information about the git status of a given package, if available. - @available(_PackageDescription, introduced: 5.11) + @available(_PackageDescription, introduced: 6.0) public static var gitInformation: GitInformation? { model.gitInformation.map { GitInformation( @@ -45,7 +45,7 @@ public struct Context { } /// Information about the git status of a given package, if available. -@available(_PackageDescription, introduced: 5.11) +@available(_PackageDescription, introduced: 6.0) public struct GitInformation { public let currentTag: String? public let currentCommit: String diff --git a/Sources/PackageLoading/TargetSourcesBuilder.swift b/Sources/PackageLoading/TargetSourcesBuilder.swift index 37012f54de7..7e036534f59 100644 --- a/Sources/PackageLoading/TargetSourcesBuilder.swift +++ b/Sources/PackageLoading/TargetSourcesBuilder.swift @@ -185,7 +185,7 @@ public struct TargetSourcesBuilder { } let additionalResources: [Resource] - if toolsVersion >= .v5_11 { + if toolsVersion >= .v6_0 { additionalResources = declaredResources.compactMap { resource in if handledResources.contains(resource.path) { return nil diff --git a/Sources/PackageLoading/ToolsVersionParser.swift b/Sources/PackageLoading/ToolsVersionParser.swift index 290556d3e05..f38e2e4eb07 100644 --- a/Sources/PackageLoading/ToolsVersionParser.swift +++ b/Sources/PackageLoading/ToolsVersionParser.swift @@ -71,10 +71,10 @@ public struct ToolsVersionParser { while let newlineIndex = string.firstIndex(where: { $0.isNewline }) { string = String(string[newlineIndex...].dropFirst()) if !string.isEmpty, let result = try? Self._parse(utf8String: string) { - if result >= ToolsVersion.v5_11 { + if result >= ToolsVersion.v6_0 { return result } else { - throw Error.backwardIncompatiblePre5_11(.toolsVersionNeedsToBeFirstLine, specifiedVersion: result) + throw Error.backwardIncompatiblePre6_0(.toolsVersionNeedsToBeFirstLine, specifiedVersion: result) } } } @@ -504,9 +504,9 @@ extension ToolsVersionParser { case unidentified } - /// Details of backward-incompatible contents with Swift tools version < 5.11. - public enum BackwardIncompatibilityPre5_11 { - /// Tools-versions on subsequent lines of the manifest are only accepted by 5.11 or later. + /// Details of backward-incompatible contents with Swift tools version < 6.0. + public enum BackwardIncompatibilityPre6_0 { + /// Tools-versions on subsequent lines of the manifest are only accepted by 6.0 or later. case toolsVersionNeedsToBeFirstLine } @@ -520,8 +520,8 @@ extension ToolsVersionParser { case malformedToolsVersionSpecification(_ malformationLocation: ToolsVersionSpecificationMalformationLocation) /// Backward-incompatible contents with Swift tools version < 5.4. case backwardIncompatiblePre5_4(_ incompatibility: BackwardIncompatibilityPre5_4, specifiedVersion: ToolsVersion) - /// Backward-incompatible contents with Swift tools version < 5.11. - case backwardIncompatiblePre5_11(_ incompatibility: BackwardIncompatibilityPre5_11, specifiedVersion: ToolsVersion) + /// Backward-incompatible contents with Swift tools version < 6.0. + case backwardIncompatiblePre6_0(_ incompatibility: BackwardIncompatibilityPre6_0, specifiedVersion: ToolsVersion) public var description: String { @@ -588,10 +588,10 @@ extension ToolsVersionParser { case .unidentified: return "the manifest is backward-incompatible with Swift < 5.4, but the package manager is unable to pinpoint the exact incompatibility; consider replacing the current Swift tools version specification with '\(specifiedVersion.specification())' to specify Swift \(specifiedVersion) as the lowest Swift version supported by the project, then move the new specification to the very beginning of this manifest file; additionally, please consider filing a bug report on https://bugs.swift.org with this file attached" } - case let .backwardIncompatiblePre5_11(incompatibility, _): + case let .backwardIncompatiblePre6_0(incompatibility, _): switch incompatibility { case .toolsVersionNeedsToBeFirstLine: - return "the manifest is backward-incompatible with Swift < 5.11 because the tools-version was specified in a subsequent line of the manifest, not the first line. Either move the tools-version specification or increase the required tools-version of your manifest" + return "the manifest is backward-incompatible with Swift < 6.0 because the tools-version was specified in a subsequent line of the manifest, not the first line. Either move the tools-version specification or increase the required tools-version of your manifest" } } diff --git a/Sources/PackageModel/ToolsVersion.swift b/Sources/PackageModel/ToolsVersion.swift index 5fe51fe4bf4..20920f7424f 100644 --- a/Sources/PackageModel/ToolsVersion.swift +++ b/Sources/PackageModel/ToolsVersion.swift @@ -31,7 +31,7 @@ public struct ToolsVersion: Equatable, Hashable, Codable, Sendable { public static let v5_8 = ToolsVersion(version: "5.8.0") public static let v5_9 = ToolsVersion(version: "5.9.0") public static let v5_10 = ToolsVersion(version: "5.10.0") - public static let v5_11 = ToolsVersion(version: "5.11.0") + public static let v6_0 = ToolsVersion(version: "6.0.0") public static let vNext = ToolsVersion(version: "999.0.0") /// The current tools version in use. diff --git a/Sources/PackagePlugin/Command.swift b/Sources/PackagePlugin/Command.swift index 1a232ab824a..1c8c7f1e887 100644 --- a/Sources/PackagePlugin/Command.swift +++ b/Sources/PackagePlugin/Command.swift @@ -43,7 +43,7 @@ public enum Command { /// was generated as if in its source directory; other files are treated /// as resources as if explicitly listed in `Package.swift` using /// `.process(...)`. - @available(_PackageDescription, introduced: 5.11) + @available(_PackageDescription, introduced: 6.0) case buildCommand( displayName: String?, executable: URL, @@ -76,7 +76,7 @@ public enum Command { /// this command was generated as if in its source directory; other /// files are treated as resources as if explicitly listed in /// `Package.swift` using `.process(...)`. - @available(_PackageDescription, introduced: 5.11) + @available(_PackageDescription, introduced: 6.0) case prebuildCommand( displayName: String?, executable: URL, @@ -114,7 +114,7 @@ public extension Command { /// was generated as if in its source directory; other files are treated /// as resources as if explicitly listed in `Package.swift` using /// `.process(...)`. - @available(_PackageDescription, deprecated: 5.11) + @available(_PackageDescription, deprecated: 6.0) static func buildCommand( displayName: String?, executable: Path, @@ -204,7 +204,7 @@ public extension Command { /// this command was generated as if in its source directory; other /// files are treated as resources as if explicitly listed in /// `Package.swift` using `.process(...)`. - @available(_PackageDescription, deprecated: 5.11) + @available(_PackageDescription, deprecated: 6.0) static func prebuildCommand( displayName: String?, executable: Path, diff --git a/Sources/PackagePlugin/Context.swift b/Sources/PackagePlugin/Context.swift index 36b5124782d..521bfc95dce 100644 --- a/Sources/PackagePlugin/Context.swift +++ b/Sources/PackagePlugin/Context.swift @@ -31,7 +31,7 @@ public struct PluginContext { /// write its outputs to that directory. The plugin may also create other /// directories for cache files and other file system content that either /// it or the command will need. - @available(_PackageDescription, deprecated: 5.11) + @available(_PackageDescription, deprecated: 6.0) public let pluginWorkDirectory: Path /// The path of a writable directory into which the plugin or the build @@ -46,7 +46,7 @@ public struct PluginContext { /// write its outputs to that directory. The plugin may also create other /// directories for cache files and other file system content that either /// it or the command will need. - @available(_PackageDescription, introduced: 5.11) + @available(_PackageDescription, introduced: 6.0) public let pluginWorkDirectoryURL: URL /// Looks up and returns the path of a named command line executable tool. @@ -86,12 +86,12 @@ public struct PluginContext { /// The paths of directories of in which to search for tools that aren't in /// the `toolNamesToPaths` map. - @available(_PackageDescription, deprecated: 5.11) + @available(_PackageDescription, deprecated: 6.0) let toolSearchDirectories: [Path] /// The paths of directories of in which to search for tools that aren't in /// the `toolNamesToPaths` map. - @available(_PackageDescription, introduced: 5.11) + @available(_PackageDescription, introduced: 6.0) let toolSearchDirectoryURLs: [URL] /// Information about a particular tool that is available to a plugin. @@ -100,11 +100,11 @@ public struct PluginContext { public let name: String /// Full path of the built or provided tool in the file system. - @available(_PackageDescription, deprecated: 5.11) + @available(_PackageDescription, deprecated: 6.0) public let path: Path /// Full path of the built or provided tool in the file system. - @available(_PackageDescription, introduced: 5.11) + @available(_PackageDescription, introduced: 6.0) public let url: URL } } diff --git a/Sources/PackagePlugin/PackageManagerProxy.swift b/Sources/PackagePlugin/PackageManagerProxy.swift index ce01036185d..8268b439512 100644 --- a/Sources/PackagePlugin/PackageManagerProxy.swift +++ b/Sources/PackagePlugin/PackageManagerProxy.swift @@ -108,13 +108,13 @@ public struct PackageManager { /// Represents a single artifact produced during a build. public struct BuiltArtifact { /// Full path of the built artifact in the local file system. - @available(_PackageDescription, deprecated: 5.11) + @available(_PackageDescription, deprecated: 6.0) public var path: Path { return Path(url: url) } /// Full path of the built artifact in the local file system. - @available(_PackageDescription, introduced: 5.11) + @available(_PackageDescription, introduced: 6.0) public var url: URL /// The kind of artifact that was built. @@ -182,14 +182,14 @@ public struct PackageManager { /// Path of a generated `.profdata` file suitable for processing using /// `llvm-cov`, if `enableCodeCoverage` was set in the test parameters. - @available(_PackageDescription, deprecated: 5.11) + @available(_PackageDescription, deprecated: 6.0) public var codeCoverageDataFile: Path? { return codeCoverageDataFileURL.map { Path(url: $0) } } /// Path of a generated `.profdata` file suitable for processing using /// `llvm-cov`, if `enableCodeCoverage` was set in the test parameters. - @available(_PackageDescription, introduced: 5.11) + @available(_PackageDescription, introduced: 6.0) public var codeCoverageDataFileURL: URL? /// Represents the results of running some or all of the tests in a @@ -265,13 +265,13 @@ public struct PackageManager { /// Represents the result of symbol graph generation. public struct SymbolGraphResult { /// The directory that contains the symbol graph files for the target. - @available(_PackageDescription, deprecated: 5.11) + @available(_PackageDescription, deprecated: 6.0) public var directoryPath: Path { return Path(url: directoryURL) } /// The directory that contains the symbol graph files for the target. - @available(_PackageDescription, introduced: 5.11) + @available(_PackageDescription, introduced: 6.0) public var directoryURL: URL } } diff --git a/Sources/PackagePlugin/PackageModel.swift b/Sources/PackagePlugin/PackageModel.swift index 99ef721d57a..fd61fe2b272 100644 --- a/Sources/PackagePlugin/PackageModel.swift +++ b/Sources/PackagePlugin/PackageModel.swift @@ -22,11 +22,11 @@ public struct Package { public let displayName: String /// The absolute path of the package directory in the local file system. - @available(_PackageDescription, deprecated: 5.11) + @available(_PackageDescription, deprecated: 6.0) public let directory: Path /// The absolute path of the package directory in the local file system. - @available(_PackageDescription, introduced: 5.11) + @available(_PackageDescription, introduced: 6.0) public let directoryURL: URL /// The origin of the package (root, local, repository, registry, etc). @@ -221,13 +221,13 @@ public protocol SourceModuleTarget: Target { /// Paths of any sources generated by other plugins that have been applied to the given target before the plugin currently being executed. /// /// Note: Plugins are applied in order of declaration in the package manifest. Generated files are vended to the target the current plugin is being applied to, but not necessarily to other targets in the package graph. - @available(_PackageDescription, introduced: 5.11) + @available(_PackageDescription, introduced: 6.0) var pluginGeneratedSources: [URL] { get } /// Paths of any resources generated by other plugins that have been applied to the given target before the plugin currently being executed. /// /// Note: Plugins are applied in order of declaration in the package manifest. Generated files are vended to the target the current plugin is being applied to, but not necessarily to other targets in the package graph. - @available(_PackageDescription, introduced: 5.11) + @available(_PackageDescription, introduced: 6.0) var pluginGeneratedResources: [URL] { get } } @@ -261,11 +261,11 @@ public struct SwiftSourceModuleTarget: SourceModuleTarget { public let kind: ModuleKind /// The absolute path of the target directory in the local file system. - @available(_PackageDescription, deprecated: 5.11) + @available(_PackageDescription, deprecated: 6.0) public let directory: Path /// The absolute path of the target directory in the local file system. - @available(_PackageDescription, introduced: 5.11) + @available(_PackageDescription, introduced: 6.0) public let directoryURL: URL /// Any other targets on which this target depends, in the same order as @@ -294,11 +294,11 @@ public struct SwiftSourceModuleTarget: SourceModuleTarget { public let linkedFrameworks: [String] /// Paths of any sources generated by other plugins that have been applied to the given target before the plugin currently being executed. - @available(_PackageDescription, introduced: 5.11) + @available(_PackageDescription, introduced: 6.0) public let pluginGeneratedSources: [URL] /// Paths of any resources generated by other plugins that have been applied to the given target before the plugin currently being executed. - @available(_PackageDescription, introduced: 5.11) + @available(_PackageDescription, introduced: 6.0) public let pluginGeneratedResources: [URL] } @@ -316,11 +316,11 @@ public struct ClangSourceModuleTarget: SourceModuleTarget { public let kind: ModuleKind /// The absolute path of the target directory in the local file system. - @available(_PackageDescription, deprecated: 5.11) + @available(_PackageDescription, deprecated: 6.0) public let directory: Path /// The absolute path of the target directory in the local file system. - @available(_PackageDescription, introduced: 5.11) + @available(_PackageDescription, introduced: 6.0) public let directoryURL: URL /// Any other targets on which this target depends, in the same order as @@ -344,12 +344,12 @@ public struct ClangSourceModuleTarget: SourceModuleTarget { /// The directory containing public C headers, if applicable. This will /// only be set for targets that have a directory of a public headers. - @available(_PackageDescription, deprecated: 5.11) + @available(_PackageDescription, deprecated: 6.0) public let publicHeadersDirectory: Path? /// The directory containing public C headers, if applicable. This will /// only be set for targets that have a directory of a public headers. - @available(_PackageDescription, deprecated: 5.11) + @available(_PackageDescription, deprecated: 6.0) public let publicHeadersDirectoryURL: URL? /// Any custom linked libraries required by the module, as specified in the @@ -361,11 +361,11 @@ public struct ClangSourceModuleTarget: SourceModuleTarget { public let linkedFrameworks: [String] /// Paths of any sources generated by other plugins that have been applied to the given target before the plugin currently being executed. - @available(_PackageDescription, introduced: 5.11) + @available(_PackageDescription, introduced: 6.0) public let pluginGeneratedSources: [URL] /// Paths of any resources generated by other plugins that have been applied to the given target before the plugin currently being executed. - @available(_PackageDescription, introduced: 5.11) + @available(_PackageDescription, introduced: 6.0) public let pluginGeneratedResources: [URL] } @@ -380,11 +380,11 @@ public struct BinaryArtifactTarget: Target { public let name: String /// The absolute path of the target directory in the local file system. - @available(_PackageDescription, deprecated: 5.11) + @available(_PackageDescription, deprecated: 6.0) public let directory: Path /// The absolute path of the target directory in the local file system. - @available(_PackageDescription, introduced: 5.11) + @available(_PackageDescription, introduced: 6.0) public let directoryURL: URL /// Any other targets on which this target depends, in the same order as @@ -399,11 +399,11 @@ public struct BinaryArtifactTarget: Target { public let origin: Origin /// The location of the binary artifact in the local file system. - @available(_PackageDescription, deprecated: 5.11) + @available(_PackageDescription, deprecated: 6.0) public let artifact: Path /// The location of the binary artifact in the local file system. - @available(_PackageDescription, introduced: 5.11) + @available(_PackageDescription, introduced: 6.0) public let artifactURL: URL /// Represents a kind of binary artifact. @@ -434,11 +434,11 @@ public struct SystemLibraryTarget: Target { public var name: String /// The absolute path of the target directory in the local file system. - @available(_PackageDescription, deprecated: 5.11) + @available(_PackageDescription, deprecated: 6.0) public var directory: Path /// The absolute path of the target directory in the local file system. - @available(_PackageDescription, introduced: 5.11) + @available(_PackageDescription, introduced: 6.0) public var directoryURL: URL /// Any other targets on which this target depends, in the same order as @@ -494,11 +494,11 @@ extension FileList: RandomAccessCollection { /// Provides information about a single file in a FileList. public struct File { /// The path of the file. - @available(_PackageDescription, deprecated: 5.11) + @available(_PackageDescription, deprecated: 6.0) public let path: Path /// The path of the file. - @available(_PackageDescription, introduced: 5.11) + @available(_PackageDescription, introduced: 6.0) public let url: URL /// File type, as determined by SwiftPM. diff --git a/Sources/PackagePlugin/Path.swift b/Sources/PackagePlugin/Path.swift index b97427f1fca..19a6e3556fe 100644 --- a/Sources/PackagePlugin/Path.swift +++ b/Sources/PackagePlugin/Path.swift @@ -18,7 +18,7 @@ public struct Path: Hashable { /// Initializes the path from the contents a string, which should be an /// absolute path in platform representation. - @available(_PackageDescription, deprecated: 5.11) + @available(_PackageDescription, deprecated: 6.0) public init(_ string: String) { self._string = string } @@ -28,7 +28,7 @@ public struct Path: Hashable { } /// A string representation of the path. - @available(_PackageDescription, deprecated: 5.11) + @available(_PackageDescription, deprecated: 6.0) public var string: String { return _string } @@ -39,7 +39,7 @@ public struct Path: Hashable { } /// The last path component (including any extension). - @available(_PackageDescription, deprecated: 5.11) + @available(_PackageDescription, deprecated: 6.0) public var lastComponent: String { // Check for a special case of the root directory. if _string == "/" { @@ -58,7 +58,7 @@ public struct Path: Hashable { } /// The last path component (without any extension). - @available(_PackageDescription, deprecated: 5.11) + @available(_PackageDescription, deprecated: 6.0) public var stem: String { let filename = self.lastComponent if let ext = self.extension { @@ -69,7 +69,7 @@ public struct Path: Hashable { } /// The filename extension, if any (without any leading dot). - @available(_PackageDescription, deprecated: 5.11) + @available(_PackageDescription, deprecated: 6.0) public var `extension`: String? { // Find the last path separator, if any. let sIdx = _string.lastIndex(of: "/") @@ -92,7 +92,7 @@ public struct Path: Hashable { } /// The path except for the last path component. - @available(_PackageDescription, deprecated: 5.11) + @available(_PackageDescription, deprecated: 6.0) public func removingLastComponent() -> Path { // Find the last path separator. guard let idx = string.lastIndex(of: "/") else { @@ -111,19 +111,19 @@ public struct Path: Hashable { /// The result of appending a subpath, which should be a relative path in /// platform representation. - @available(_PackageDescription, deprecated: 5.11) + @available(_PackageDescription, deprecated: 6.0) public func appending(subpath: String) -> Path { return Path(_string + (_string.hasSuffix("/") ? "" : "/") + subpath) } /// The result of appending one or more path components. - @available(_PackageDescription, deprecated: 5.11) + @available(_PackageDescription, deprecated: 6.0) public func appending(_ components: [String]) -> Path { return self.appending(subpath: components.joined(separator: "/")) } /// The result of appending one or more path components. - @available(_PackageDescription, deprecated: 5.11) + @available(_PackageDescription, deprecated: 6.0) public func appending(_ components: String...) -> Path { return self.appending(components) } @@ -131,7 +131,7 @@ public struct Path: Hashable { extension Path: CustomStringConvertible { - @available(_PackageDescription, deprecated: 5.11) + @available(_PackageDescription, deprecated: 6.0) public var description: String { return self.string } @@ -139,13 +139,13 @@ extension Path: CustomStringConvertible { extension Path: Codable { - @available(_PackageDescription, deprecated: 5.11) + @available(_PackageDescription, deprecated: 6.0) public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() try container.encode(self.string) } - @available(_PackageDescription, deprecated: 5.11) + @available(_PackageDescription, deprecated: 6.0) public init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() let string = try container.decode(String.self) @@ -155,7 +155,7 @@ extension Path: Codable { public extension String.StringInterpolation { - @available(_PackageDescription, deprecated: 5.11) + @available(_PackageDescription, deprecated: 6.0) mutating func appendInterpolation(_ path: Path) { self.appendInterpolation(path.string) } diff --git a/Sources/PackageRegistryTool/PackageRegistryTool+Publish.swift b/Sources/PackageRegistryTool/PackageRegistryTool+Publish.swift index b688d824772..c698c2294b0 100644 --- a/Sources/PackageRegistryTool/PackageRegistryTool+Publish.swift +++ b/Sources/PackageRegistryTool/PackageRegistryTool+Publish.swift @@ -247,7 +247,7 @@ extension SignatureFormat { } } -#if swift(<5.11) +#if swift(<6.0) extension SignatureFormat: ExpressibleByArgument {} #else extension SignatureFormat: @retroactive ExpressibleByArgument {} diff --git a/Sources/SPMBuildCore/Plugins/PluginInvocation.swift b/Sources/SPMBuildCore/Plugins/PluginInvocation.swift index 72dcef42431..c117fe2528e 100644 --- a/Sources/SPMBuildCore/Plugins/PluginInvocation.swift +++ b/Sources/SPMBuildCore/Plugins/PluginInvocation.swift @@ -529,10 +529,10 @@ extension PackageGraph { } let delegate = PluginDelegate(fileSystem: fileSystem, delegateQueue: delegateQueue, toolPaths: toolPaths, builtToolNames: builtToolNames) - // In tools version 5.11 and newer, we vend the list of files generated by previous plugins. + // In tools version 6.0 and newer, we vend the list of files generated by previous plugins. let pluginDerivedSources: Sources let pluginDerivedResources: [Resource] - if package.manifest.toolsVersion >= .v5_11 { + if package.manifest.toolsVersion >= .v6_0 { // Set up dummy observability because we don't want to emit diagnostics for this before the actual build. let observability = ObservabilitySystem({ _, _ in }) // Compute the generated files based on all results we have computed so far. diff --git a/Sources/SPMTestSupport/misc.swift b/Sources/SPMTestSupport/misc.swift index 2c357dcc375..278ee54bbde 100644 --- a/Sources/SPMTestSupport/misc.swift +++ b/Sources/SPMTestSupport/misc.swift @@ -458,7 +458,7 @@ extension InitPackage { } } -#if swift(<5.11) +#if swift(<6.0) extension RelativePath: ExpressibleByStringLiteral {} extension RelativePath: ExpressibleByStringInterpolation {} extension URL: ExpressibleByStringLiteral {} diff --git a/Sources/Workspace/Diagnostics.swift b/Sources/Workspace/Diagnostics.swift index 8e52c7dd575..8bf918875bc 100644 --- a/Sources/Workspace/Diagnostics.swift +++ b/Sources/Workspace/Diagnostics.swift @@ -247,7 +247,7 @@ extension FileSystemError { } } -#if swift(<5.11) +#if swift(<6.0) extension FileSystemError: CustomStringConvertible {} #else extension FileSystemError: @retroactive CustomStringConvertible {} diff --git a/Sources/Workspace/Workspace.swift b/Sources/Workspace/Workspace.swift index ea93cf4ece4..3a57b162478 100644 --- a/Sources/Workspace/Workspace.swift +++ b/Sources/Workspace/Workspace.swift @@ -1471,7 +1471,7 @@ private func warnToStderr(_ message: String) { } // used for manifest validation -#if swift(<5.11) +#if swift(<6.0) extension RepositoryManager: ManifestSourceControlValidator {} #else extension RepositoryManager: @retroactive ManifestSourceControlValidator {} diff --git a/Sources/swift-bootstrap/main.swift b/Sources/swift-bootstrap/main.swift index 02ceae1ca1e..f29942cb356 100644 --- a/Sources/swift-bootstrap/main.swift +++ b/Sources/swift-bootstrap/main.swift @@ -481,7 +481,7 @@ extension BuildConfiguration { } } -#if swift(<5.11) +#if swift(<6.0) extension AbsolutePath: ExpressibleByArgument {} extension BuildConfiguration: ExpressibleByArgument {} #else diff --git a/Tests/FunctionalTests/PluginTests.swift b/Tests/FunctionalTests/PluginTests.swift index 5636c6dd41b..6118eae783d 100644 --- a/Tests/FunctionalTests/PluginTests.swift +++ b/Tests/FunctionalTests/PluginTests.swift @@ -230,7 +230,7 @@ class PluginTests: XCTestCase { XCTAssertTrue(stderr.contains("warning: Build tool command 'empty' (applied to target 'SomeTarget') does not declare any output files"), "expected warning not emitted") XCTAssertFalse(localFileSystem.exists(pathOfGeneratedFile), "plugin generated file unexpectedly exists at \(pathOfGeneratedFile.pathString)") - try createPackageUnderTest(packageDir: packageDir, toolsVersion: .v5_11) + try createPackageUnderTest(packageDir: packageDir, toolsVersion: .v6_0) let (_, stderr2) = try executeSwiftBuild(packageDir) XCTAssertEqual("", stderr2) XCTAssertTrue(localFileSystem.exists(pathOfGeneratedFile), "plugin did not run, generated file does not exist at \(pathOfGeneratedFile.pathString)") diff --git a/Tests/FunctionalTests/ResourcesTests.swift b/Tests/FunctionalTests/ResourcesTests.swift index f37e531ad0e..85c10611032 100644 --- a/Tests/FunctionalTests/ResourcesTests.swift +++ b/Tests/FunctionalTests/ResourcesTests.swift @@ -135,7 +135,7 @@ class ResourcesTests: XCTestCase { try localFileSystem.writeFileContents( manifestFile, string: """ - // swift-tools-version: 5.11 + // swift-tools-version: 6.0 import PackageDescription let package = Package(name: "MyPackage", targets: [ diff --git a/Tests/FunctionalTests/ToolsVersionTests.swift b/Tests/FunctionalTests/ToolsVersionTests.swift index 067f6b893c8..4b77ed0b7e4 100644 --- a/Tests/FunctionalTests/ToolsVersionTests.swift +++ b/Tests/FunctionalTests/ToolsVersionTests.swift @@ -18,8 +18,7 @@ import SPMTestSupport import Workspace import XCTest -class ToolsVersionTests: XCTestCase { - +final class ToolsVersionTests: XCTestCase { func testToolsVersion() throws { try testWithTemporaryDirectory{ path in let fs = localFileSystem @@ -124,6 +123,7 @@ class ToolsVersionTests: XCTestCase { XCTAssertTrue(error.stderr.contains("package 'primary' requires minimum Swift language version 1000 which is not supported by the current tools version (\(ToolsVersion.current))"), error.stderr) } +#if compiler(>=6.0) try fs.writeFileContents( primaryPath.appending("Package.swift"), string: """ @@ -138,6 +138,7 @@ class ToolsVersionTests: XCTestCase { _ = try SwiftPM.Package.execute( ["tools-version", "--set", "4.2"], packagePath: primaryPath).stdout.spm_chomp() _ = try SwiftPM.Build.execute(packagePath: primaryPath) +#endif } } } diff --git a/Tests/PackageGraphPerformanceTests/DependencyResolverPerfTests.swift b/Tests/PackageGraphPerformanceTests/DependencyResolverPerfTests.swift index e5c0998676d..124ddc706be 100644 --- a/Tests/PackageGraphPerformanceTests/DependencyResolverPerfTests.swift +++ b/Tests/PackageGraphPerformanceTests/DependencyResolverPerfTests.swift @@ -201,7 +201,7 @@ extension ProductFilter { } } -#if swift(<5.11) +#if swift(<6.0) extension ProductFilter: JSONSerializable, JSONMappable {} #else extension ProductFilter: @retroactive JSONSerializable, @retroactive JSONMappable {} diff --git a/Tests/PackageGraphPerformanceTests/Inputs/kitura.json b/Tests/PackageGraphPerformanceTests/Inputs/kitura.json index 1fe7e8f72b5..c3fd458a10a 100644 --- a/Tests/PackageGraphPerformanceTests/Inputs/kitura.json +++ b/Tests/PackageGraphPerformanceTests/Inputs/kitura.json @@ -118,7 +118,7 @@ "0.5.0": [], "0.5.1": [], "0.5.10": [], - "0.5.11": [], + "0.6.0": [], "0.5.12": [], "0.5.13": [], "0.5.14": [], diff --git a/Tests/PackageGraphTests/PubgrubTests.swift b/Tests/PackageGraphTests/PubgrubTests.swift index 1cc30ae0cd6..b05bd760340 100644 --- a/Tests/PackageGraphTests/PubgrubTests.swift +++ b/Tests/PackageGraphTests/PubgrubTests.swift @@ -3361,7 +3361,7 @@ extension PackageReference { } } -#if swift(<5.11) +#if swift(<6.0) extension Term: ExpressibleByStringLiteral {} extension PackageReference: ExpressibleByStringLiteral {} #else diff --git a/Tests/PackageGraphTests/TopologicalSortTests.swift b/Tests/PackageGraphTests/TopologicalSortTests.swift index a41da57d0d4..01b33e1d413 100644 --- a/Tests/PackageGraphTests/TopologicalSortTests.swift +++ b/Tests/PackageGraphTests/TopologicalSortTests.swift @@ -34,7 +34,7 @@ extension Int { public var id: Self { self } } -#if swift(<5.11) +#if swift(<6.0) extension Int: Identifiable {} #else extension Int: @retroactive Identifiable {} diff --git a/Tests/PackageLoadingTests/PD_5_11_LoadingTests.swift b/Tests/PackageLoadingTests/PD_6_0_LoadingTests.swift similarity index 97% rename from Tests/PackageLoadingTests/PD_5_11_LoadingTests.swift rename to Tests/PackageLoadingTests/PD_6_0_LoadingTests.swift index f394ac1bbda..41602560bdb 100644 --- a/Tests/PackageLoadingTests/PD_5_11_LoadingTests.swift +++ b/Tests/PackageLoadingTests/PD_6_0_LoadingTests.swift @@ -16,9 +16,9 @@ import SourceControl import SPMTestSupport import XCTest -class PackageDescription5_11LoadingTests: PackageDescriptionLoadingTests { +class PackageDescription6_0LoadingTests: PackageDescriptionLoadingTests { override var toolsVersion: ToolsVersion { - .v5_11 + .v6_0 } func testPackageContextGitStatus() async throws { diff --git a/Tests/PackageLoadingTests/PackageBuilderTests.swift b/Tests/PackageLoadingTests/PackageBuilderTests.swift index 6d530c4cf62..587439049b1 100644 --- a/Tests/PackageLoadingTests/PackageBuilderTests.swift +++ b/Tests/PackageLoadingTests/PackageBuilderTests.swift @@ -1963,15 +1963,36 @@ final class PackageBuilderTests: XCTestCase { package.checkProduct("foo") { _ in } } + manifest = try createManifest(swiftVersions: [SwiftLanguageVersion(string: "5")!]) + PackageBuilderTester(manifest, in: fs) { package, diagnostics in + package.checkModule("foo") { module in + module.check(swiftVersion: "5") + } + package.checkProduct("foo") { _ in } + } + + manifest = try createManifest(swiftVersions: [SwiftLanguageVersion(string: "6")!]) + PackageBuilderTester(manifest, in: fs) { package, diagnostics in + package.checkModule("foo") { module in + module.check(swiftVersion: "6") + } + package.checkProduct("foo") { _ in } + } + manifest = try createManifest(swiftVersions: []) PackageBuilderTester(manifest, in: fs) { package, diagnostics in - diagnostics.check(diagnostic: "package '\(package.packageIdentity)' supported Swift language versions is empty", severity: .error) + diagnostics.check( + diagnostic: "package '\(package.packageIdentity)' supported Swift language versions is empty", + severity: .error + ) } - manifest = try createManifest( - swiftVersions: [SwiftLanguageVersion(string: "6")!, SwiftLanguageVersion(string: "7")!]) + manifest = try createManifest(swiftVersions: [SwiftLanguageVersion(string: "7")!]) PackageBuilderTester(manifest, in: fs) { package, diagnostics in - diagnostics.check(diagnostic: "package '\(package.packageIdentity)' requires minimum Swift language version 6 which is not supported by the current tools version (\(ToolsVersion.current))", severity: .error) + diagnostics.check( + diagnostic: "package '\(package.packageIdentity)' requires minimum Swift language version 7 which is not supported by the current tools version (\(ToolsVersion.current))", + severity: .error + ) } } diff --git a/Tests/PackageLoadingTests/ToolsVersionParserTests.swift b/Tests/PackageLoadingTests/ToolsVersionParserTests.swift index bf4145dfbdc..486828a7254 100644 --- a/Tests/PackageLoadingTests/ToolsVersionParserTests.swift +++ b/Tests/PackageLoadingTests/ToolsVersionParserTests.swift @@ -154,12 +154,12 @@ class ToolsVersionParserTests: XCTestCase { """ // comment 1 // comment 2 - // swift-tools-version: 5.11 + // swift-tools-version: 6.0 // comment let package = .. """ ) { toolsVersion in - XCTAssertEqual(toolsVersion.description, "5.11.0") + XCTAssertEqual(toolsVersion.description, "6.0.0") } do { @@ -174,7 +174,7 @@ class ToolsVersionParserTests: XCTestCase { ) { _ in XCTFail("expected an error to be thrown") } - } catch ToolsVersionParser.Error.backwardIncompatiblePre5_11(let incompatibility, _) { + } catch ToolsVersionParser.Error.backwardIncompatiblePre6_0(let incompatibility, _) { XCTAssertEqual(incompatibility, .toolsVersionNeedsToBeFirstLine) } catch { XCTFail("unexpected error: \(error)") @@ -201,12 +201,12 @@ class ToolsVersionParserTests: XCTestCase { /* this is a multiline comment */ - // swift-tools-version: 5.11 + // swift-tools-version: 6.0 // comment let package = .. """ ) { toolsVersion in - XCTAssertEqual(toolsVersion.description, "5.11.0") + XCTAssertEqual(toolsVersion.description, "6.0.0") } } From 6ff5cbdfa8b694525b2223a6b832cce17e0b73ef Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 23 Feb 2024 18:56:59 +0000 Subject: [PATCH 023/159] Rename `PackageGraph` type to `ModulesGraph` (#7363) We have to operate on two different graphs in the `PackageGraph` module: 1. A graph of packages in PubGrub package resolution code; 2. A graph of resolved products and modules that belong to those products. Currently the second graph is misleadingly called `PackageGraph`, although that name is much more suitable for the first graph. This naming is confusing and unfortunate, and can be especially misleading for first-time contributors. We should better document the SwiftPM resolution and build pipeline in the future, but cleaning up naming is the first step. I'm keeping old names as deprecated overloads or typealiases to make migration easier for SwiftPM clients. This renaming has no impact on any public stable modules like `PackageDescription`. In the short term we should also split the `PackageGraph` module into `PubGrub` and `ModulesGraph` modules, but for now I'm renaming a single type to keep the PR manageable. --- .../ClangTargetBuildDescription.swift | 4 +- .../SwiftTargetBuildDescription.swift | 2 +- Sources/Build/BuildOperation.swift | 12 +- Sources/Build/BuildPlan/BuildPlan+Test.swift | 4 +- Sources/Build/BuildPlan/BuildPlan.swift | 6 +- Sources/Commands/PackageTools/APIDiff.swift | 2 +- .../Commands/PackageTools/PluginCommand.swift | 6 +- Sources/Commands/SwiftRunTool.swift | 2 +- Sources/Commands/Utilities/APIDigester.swift | 2 +- Sources/CoreCommands/BuildSystemSupport.swift | 6 +- Sources/CoreCommands/SwiftTool.swift | 4 +- Sources/PackageGraph/CMakeLists.txt | 4 +- ...ading.swift => ModulesGraph+Loading.swift} | 6 +- ...{PackageGraph.swift => ModulesGraph.swift} | 9 +- .../BuildSystem/BuildSystem.swift | 6 +- .../Plugins/PluginInvocation.swift | 6 +- .../SPMTestSupport/MockPackageGraphs.swift | 4 +- Sources/SPMTestSupport/MockWorkspace.swift | 4 +- .../SPMTestSupport/PackageGraphTester.swift | 6 +- Sources/SPMTestSupport/misc.swift | 35 ++++- Sources/Workspace/Workspace+Signing.swift | 4 +- Sources/Workspace/Workspace.swift | 14 +- Sources/XCBuildSupport/PIFBuilder.swift | 12 +- Sources/XCBuildSupport/XcodeBuildSystem.swift | 8 +- Sources/swift-bootstrap/main.swift | 4 +- Tests/BuildTests/BuildPlanTests.swift | 126 +++++++++--------- .../LLBuildManifestBuilderTests.swift | 2 +- .../BuildTests/ModuleAliasingBuildTests.swift | 80 +++++------ .../ProductBuildDescriptionTests.swift | 4 +- .../PackageGraphPerfTests.swift | 2 +- ...aphTests.swift => ModulesGraphTests.swift} | 112 ++++++++-------- .../SourceKitLSPAPITests.swift | 2 +- .../XCBuildSupportTests/PIFBuilderTests.swift | 32 ++--- 33 files changed, 288 insertions(+), 244 deletions(-) rename Sources/PackageGraph/{PackageGraph+Loading.swift => ModulesGraph+Loading.swift} (99%) rename Sources/PackageGraph/{PackageGraph.swift => ModulesGraph.swift} (98%) rename Tests/PackageGraphTests/{PackageGraphTests.swift => ModulesGraphTests.swift} (97%) diff --git a/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift b/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift index 495e9f2419a..90d37912ab8 100644 --- a/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift @@ -13,7 +13,7 @@ import Basics import PackageLoading import PackageModel -import struct PackageGraph.PackageGraph +import struct PackageGraph.ModulesGraph import struct PackageGraph.ResolvedTarget import struct SPMBuildCore.BuildParameters import struct SPMBuildCore.BuildToolPluginInvocationResult @@ -134,7 +134,7 @@ public final class ClangTargetBuildDescription { if toolsVersion >= .v5_9 { self.buildToolPluginInvocationResults = buildToolPluginInvocationResults - (self.pluginDerivedSources, self.pluginDerivedResources) = PackageGraph.computePluginGeneratedFiles( + (self.pluginDerivedSources, self.pluginDerivedResources) = ModulesGraph.computePluginGeneratedFiles( target: target, toolsVersion: toolsVersion, additionalFileRules: additionalFileRules, diff --git a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift index a7216c99aa0..206bf6b163c 100644 --- a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift @@ -287,7 +287,7 @@ public final class SwiftTargetBuildDescription { self.fileSystem = fileSystem self.observabilityScope = observabilityScope - (self.pluginDerivedSources, self.pluginDerivedResources) = PackageGraph.computePluginGeneratedFiles( + (self.pluginDerivedSources, self.pluginDerivedResources) = ModulesGraph.computePluginGeneratedFiles( target: target, toolsVersion: toolsVersion, additionalFileRules: additionalFileRules, diff --git a/Sources/Build/BuildOperation.swift b/Sources/Build/BuildOperation.swift index fab2429cccf..3d92d1676c2 100644 --- a/Sources/Build/BuildOperation.swift +++ b/Sources/Build/BuildOperation.swift @@ -47,7 +47,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS let toolsBuildParameters: BuildParameters /// The closure for loading the package graph. - let packageGraphLoader: () throws -> PackageGraph + let packageGraphLoader: () throws -> ModulesGraph /// the plugin configuration for build plugins let pluginConfiguration: PluginConfiguration? @@ -78,7 +78,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS private let buildDescription = ThreadSafeBox() /// The loaded package graph. - private let packageGraph = ThreadSafeBox() + private let packageGraph = ThreadSafeBox() /// The output stream for the build delegate. private let outputStream: OutputByteStream @@ -112,7 +112,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS productsBuildParameters: BuildParameters, toolsBuildParameters: BuildParameters, cacheBuildManifest: Bool, - packageGraphLoader: @escaping () throws -> PackageGraph, + packageGraphLoader: @escaping () throws -> ModulesGraph, pluginConfiguration: PluginConfiguration? = .none, additionalFileRules: [FileRuleDescription], pkgConfigDirectories: [AbsolutePath], @@ -145,7 +145,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS self.observabilityScope = observabilityScope.makeChildScope(description: "Build Operation") } - public func getPackageGraph() throws -> PackageGraph { + public func getPackageGraph() throws -> ModulesGraph { try self.packageGraph.memoize { try self.packageGraphLoader() } @@ -877,7 +877,7 @@ extension BuildDescription { } extension BuildSubset { - func recursiveDependencies(for graph: PackageGraph, observabilityScope: ObservabilityScope) throws -> [ResolvedTarget]? { + func recursiveDependencies(for graph: ModulesGraph, observabilityScope: ObservabilityScope) throws -> [ResolvedTarget]? { switch self { case .allIncludingTests: return Array(graph.reachableTargets) @@ -899,7 +899,7 @@ extension BuildSubset { } /// Returns the name of the llbuild target that corresponds to the build subset. - func llbuildTargetName(for graph: PackageGraph, config: String, observabilityScope: ObservabilityScope) + func llbuildTargetName(for graph: ModulesGraph, config: String, observabilityScope: ObservabilityScope) -> String? { switch self { diff --git a/Sources/Build/BuildPlan/BuildPlan+Test.swift b/Sources/Build/BuildPlan/BuildPlan+Test.swift index 93fd9da29c7..58001c2816f 100644 --- a/Sources/Build/BuildPlan/BuildPlan+Test.swift +++ b/Sources/Build/BuildPlan/BuildPlan+Test.swift @@ -15,7 +15,7 @@ import struct Basics.InternalError import struct Basics.AbsolutePath import struct LLBuildManifest.TestDiscoveryTool import struct LLBuildManifest.TestEntryPointTool -import struct PackageGraph.PackageGraph +import struct PackageGraph.ModulesGraph import struct PackageGraph.ResolvedProduct import struct PackageGraph.ResolvedTarget import struct PackageModel.Sources @@ -27,7 +27,7 @@ import protocol TSCBasic.FileSystem extension BuildPlan { static func makeDerivedTestTargets( _ buildParameters: BuildParameters, - _ graph: PackageGraph, + _ graph: ModulesGraph, _ disableSandbox: Bool, _ fileSystem: FileSystem, _ observabilityScope: ObservabilityScope diff --git a/Sources/Build/BuildPlan/BuildPlan.swift b/Sources/Build/BuildPlan/BuildPlan.swift index 1f4eda79e5f..0234675dcf7 100644 --- a/Sources/Build/BuildPlan/BuildPlan.swift +++ b/Sources/Build/BuildPlan/BuildPlan.swift @@ -196,7 +196,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan { } /// The package graph. - public let graph: PackageGraph + public let graph: ModulesGraph /// The target build description map. public let targetMap: [ResolvedTarget.ID: TargetBuildDescription] @@ -249,7 +249,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan { @available(*, deprecated, renamed: "init(productsBuildParameters:toolsBuildParameters:graph:)") public convenience init( buildParameters: BuildParameters, - graph: PackageGraph, + graph: ModulesGraph, additionalFileRules: [FileRuleDescription] = [], buildToolPluginInvocationResults: [ResolvedTarget.ID: [BuildToolPluginInvocationResult]] = [:], prebuildCommandResults: [ResolvedTarget.ID: [PrebuildCommandResult]] = [:], @@ -272,7 +272,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan { public init( productsBuildParameters: BuildParameters, toolsBuildParameters: BuildParameters, - graph: PackageGraph, + graph: ModulesGraph, additionalFileRules: [FileRuleDescription] = [], buildToolPluginInvocationResults: [ResolvedTarget.ID: [BuildToolPluginInvocationResult]] = [:], prebuildCommandResults: [ResolvedTarget.ID: [PrebuildCommandResult]] = [:], diff --git a/Sources/Commands/PackageTools/APIDiff.swift b/Sources/Commands/PackageTools/APIDiff.swift index 3eeb2b813ca..bdcc0826525 100644 --- a/Sources/Commands/PackageTools/APIDiff.swift +++ b/Sources/Commands/PackageTools/APIDiff.swift @@ -160,7 +160,7 @@ struct APIDiff: SwiftCommand { } } - private func determineModulesToDiff(packageGraph: PackageGraph, observabilityScope: ObservabilityScope) throws -> Set { + private func determineModulesToDiff(packageGraph: ModulesGraph, observabilityScope: ObservabilityScope) throws -> Set { var modulesToDiff: Set = [] if products.isEmpty && targets.isEmpty { modulesToDiff.formUnion(packageGraph.apiDigesterModules) diff --git a/Sources/Commands/PackageTools/PluginCommand.swift b/Sources/Commands/PackageTools/PluginCommand.swift index 1968dae8183..e1df26adaba 100644 --- a/Sources/Commands/PackageTools/PluginCommand.swift +++ b/Sources/Commands/PackageTools/PluginCommand.swift @@ -211,7 +211,7 @@ struct PluginCommand: SwiftCommand { static func run( plugin: PluginTarget, package: ResolvedPackage, - packageGraph: PackageGraph, + packageGraph: ModulesGraph, options: PluginOptions, arguments: [String], swiftTool: SwiftTool @@ -369,7 +369,7 @@ struct PluginCommand: SwiftCommand { // TODO: We should also emit a final line of output regarding the result. } - static func availableCommandPlugins(in graph: PackageGraph, limitedTo packageIdentity: String?) -> [PluginTarget] { + static func availableCommandPlugins(in graph: ModulesGraph, limitedTo packageIdentity: String?) -> [PluginTarget] { // All targets from plugin products of direct dependencies are "available". let directDependencyPackages = graph.rootPackages.flatMap { $0.dependencies }.filter { $0.matching(identity: packageIdentity) } let directDependencyPluginTargets = directDependencyPackages.flatMap { $0.products.filter { $0.type == .plugin } }.flatMap { $0.targets } @@ -383,7 +383,7 @@ struct PluginCommand: SwiftCommand { } } - static func findPlugins(matching verb: String, in graph: PackageGraph, limitedTo packageIdentity: String?) -> [PluginTarget] { + static func findPlugins(matching verb: String, in graph: ModulesGraph, limitedTo packageIdentity: String?) -> [PluginTarget] { // Find and return the command plugins that match the command. Self.availableCommandPlugins(in: graph, limitedTo: packageIdentity).filter { // Filter out any non-command plugins and any whose verb is different. diff --git a/Sources/Commands/SwiftRunTool.swift b/Sources/Commands/SwiftRunTool.swift index edc89214c1a..d9a9c91a4c3 100644 --- a/Sources/Commands/SwiftRunTool.swift +++ b/Sources/Commands/SwiftRunTool.swift @@ -216,7 +216,7 @@ public struct SwiftRunTool: SwiftCommand { } /// Returns the path to the correct executable based on options. - private func findProductName(in graph: PackageGraph) throws -> String { + private func findProductName(in graph: ModulesGraph) throws -> String { if let executable = options.executable { let executableExists = graph.allProducts.contains { ($0.type == .executable || $0.type == .snippet) && $0.name == executable } guard executableExists else { diff --git a/Sources/Commands/Utilities/APIDigester.swift b/Sources/Commands/Utilities/APIDigester.swift index b69465eb483..cfc5438d48d 100644 --- a/Sources/Commands/Utilities/APIDigester.swift +++ b/Sources/Commands/Utilities/APIDigester.swift @@ -310,7 +310,7 @@ extension BuildParameters { } } -extension PackageGraph { +extension ModulesGraph { /// The list of modules that should be used as an input to the API digester. var apiDigesterModules: [String] { self.rootPackages diff --git a/Sources/CoreCommands/BuildSystemSupport.swift b/Sources/CoreCommands/BuildSystemSupport.swift index ec96b4170df..c0f48b325e3 100644 --- a/Sources/CoreCommands/BuildSystemSupport.swift +++ b/Sources/CoreCommands/BuildSystemSupport.swift @@ -18,7 +18,7 @@ import XCBuildSupport #endif import class Basics.ObservabilityScope -import struct PackageGraph.PackageGraph +import struct PackageGraph.ModulesGraph import struct PackageLoading.FileRuleDescription import protocol TSCBasic.OutputByteStream @@ -30,7 +30,7 @@ private struct NativeBuildSystemFactory: BuildSystemFactory { cacheBuildManifest: Bool, productsBuildParameters: BuildParameters?, toolsBuildParameters: BuildParameters?, - packageGraphLoader: (() throws -> PackageGraph)?, + packageGraphLoader: (() throws -> ModulesGraph)?, outputStream: OutputByteStream?, logLevel: Diagnostic.Severity?, observabilityScope: ObservabilityScope? @@ -72,7 +72,7 @@ private struct XcodeBuildSystemFactory: BuildSystemFactory { cacheBuildManifest: Bool, productsBuildParameters: BuildParameters?, toolsBuildParameters: BuildParameters?, - packageGraphLoader: (() throws -> PackageGraph)?, + packageGraphLoader: (() throws -> ModulesGraph)?, outputStream: OutputByteStream?, logLevel: Diagnostic.Severity?, observabilityScope: ObservabilityScope? diff --git a/Sources/CoreCommands/SwiftTool.swift b/Sources/CoreCommands/SwiftTool.swift index 6126f8ca286..478dbc253de 100644 --- a/Sources/CoreCommands/SwiftTool.swift +++ b/Sources/CoreCommands/SwiftTool.swift @@ -594,7 +594,7 @@ public final class SwiftTool { public func loadPackageGraph( explicitProduct: String? = nil, testEntryPointPath: AbsolutePath? = nil - ) throws -> PackageGraph { + ) throws -> ModulesGraph { do { let workspace = try getActiveWorkspace() @@ -684,7 +684,7 @@ public final class SwiftTool { shouldLinkStaticSwiftStdlib: Bool = false, productsBuildParameters: BuildParameters? = .none, toolsBuildParameters: BuildParameters? = .none, - packageGraphLoader: (() throws -> PackageGraph)? = .none, + packageGraphLoader: (() throws -> ModulesGraph)? = .none, outputStream: OutputByteStream? = .none, logLevel: Basics.Diagnostic.Severity? = .none, observabilityScope: ObservabilityScope? = .none diff --git a/Sources/PackageGraph/CMakeLists.txt b/Sources/PackageGraph/CMakeLists.txt index 9d6e1f5bcae..c0910900ec9 100644 --- a/Sources/PackageGraph/CMakeLists.txt +++ b/Sources/PackageGraph/CMakeLists.txt @@ -13,9 +13,9 @@ add_library(PackageGraph Diagnostics.swift GraphLoadingNode.swift ModuleAliasTracker.swift + ModulesGraph.swift + ModulesGraph+Loading.swift PackageContainer.swift - PackageGraph.swift - PackageGraph+Loading.swift PackageGraphRoot.swift PackageModel+Extensions.swift PackageRequirement.swift diff --git a/Sources/PackageGraph/PackageGraph+Loading.swift b/Sources/PackageGraph/ModulesGraph+Loading.swift similarity index 99% rename from Sources/PackageGraph/PackageGraph+Loading.swift rename to Sources/PackageGraph/ModulesGraph+Loading.swift index 420e1ccce67..a056875684a 100644 --- a/Sources/PackageGraph/PackageGraph+Loading.swift +++ b/Sources/PackageGraph/ModulesGraph+Loading.swift @@ -17,7 +17,7 @@ import PackageModel import func TSCBasic.bestMatch -extension PackageGraph { +extension ModulesGraph { /// Load the package graph for the given package path. public static func load( root: PackageGraphRoot, @@ -35,7 +35,7 @@ extension PackageGraph { availableLibraries: [LibraryMetadata], fileSystem: FileSystem, observabilityScope: ObservabilityScope - ) throws -> PackageGraph { + ) throws -> ModulesGraph { let observabilityScope = observabilityScope.makeChildScope(description: "Loading Package Graph") @@ -168,7 +168,7 @@ extension PackageGraph { let rootPackages = resolvedPackages.filter{ root.manifests.values.contains($0.manifest) } checkAllDependenciesAreUsed(rootPackages, observabilityScope: observabilityScope) - return try PackageGraph( + return try ModulesGraph( rootPackages: rootPackages, rootDependencies: resolvedPackages.filter{ rootDependencies.contains($0.manifest) }, dependencies: requiredDependencies, diff --git a/Sources/PackageGraph/PackageGraph.swift b/Sources/PackageGraph/ModulesGraph.swift similarity index 98% rename from Sources/PackageGraph/PackageGraph.swift rename to Sources/PackageGraph/ModulesGraph.swift index bd3446b0499..471d6192e5e 100644 --- a/Sources/PackageGraph/PackageGraph.swift +++ b/Sources/PackageGraph/ModulesGraph.swift @@ -49,8 +49,15 @@ enum PackageGraphError: Swift.Error { aliases: [String]) } +@available(*, + deprecated, + renamed: "ModulesGraph", + message: "PackageGraph had a misleading name, it's a graph of dependencies between modules, not just packages" +) +public typealias PackageGraph = ModulesGraph + /// A collection of packages. -public struct PackageGraph { +public struct ModulesGraph { /// The root packages. public let rootPackages: IdentifiableSet diff --git a/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift b/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift index e8d1fd3efe1..8725be2e786 100644 --- a/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift +++ b/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift @@ -42,7 +42,7 @@ public protocol BuildSystem: Cancellable { var builtTestProducts: [BuiltTestProduct] { get } /// Returns the package graph used by the build system. - func getPackageGraph() throws -> PackageGraph + func getPackageGraph() throws -> ModulesGraph /// Builds a subset of the package graph. /// - Parameters: @@ -120,7 +120,7 @@ public protocol BuildSystemFactory { cacheBuildManifest: Bool, productsBuildParameters: BuildParameters?, toolsBuildParameters: BuildParameters?, - packageGraphLoader: (() throws -> PackageGraph)?, + packageGraphLoader: (() throws -> ModulesGraph)?, outputStream: OutputByteStream?, logLevel: Diagnostic.Severity?, observabilityScope: ObservabilityScope? @@ -146,7 +146,7 @@ public struct BuildSystemProvider { cacheBuildManifest: Bool = true, productsBuildParameters: BuildParameters? = .none, toolsBuildParameters: BuildParameters? = .none, - packageGraphLoader: (() throws -> PackageGraph)? = .none, + packageGraphLoader: (() throws -> ModulesGraph)? = .none, outputStream: OutputByteStream? = .none, logLevel: Diagnostic.Severity? = .none, observabilityScope: ObservabilityScope? = .none diff --git a/Sources/SPMBuildCore/Plugins/PluginInvocation.swift b/Sources/SPMBuildCore/Plugins/PluginInvocation.swift index c117fe2528e..435770e804a 100644 --- a/Sources/SPMBuildCore/Plugins/PluginInvocation.swift +++ b/Sources/SPMBuildCore/Plugins/PluginInvocation.swift @@ -366,7 +366,7 @@ fileprivate extension PluginToHostMessage { } } -extension PackageGraph { +extension ModulesGraph { /// Traverses the graph of reachable targets in a package graph, and applies plugins to targets as needed. Each /// plugin is passed an input context that provides information about the target to which it is being applied @@ -660,7 +660,7 @@ public extension PluginTarget { } /// The set of tools that are accessible to this plugin. - private func accessibleTools(packageGraph: PackageGraph, fileSystem: FileSystem, environment: BuildEnvironment, for hostTriple: Triple) throws -> Set { + private func accessibleTools(packageGraph: ModulesGraph, fileSystem: FileSystem, environment: BuildEnvironment, for hostTriple: Triple) throws -> Set { return try Set(self.dependencies(satisfying: environment).flatMap { dependency -> [PluginAccessibleTool] in let builtToolName: String let executableOrBinaryTarget: Target @@ -695,7 +695,7 @@ public extension PluginTarget { }) } - func processAccessibleTools(packageGraph: PackageGraph, fileSystem: FileSystem, environment: BuildEnvironment, for hostTriple: Triple, builtToolHandler: (_ name: String, _ path: RelativePath) throws -> AbsolutePath?) throws -> [String: (path: AbsolutePath, triples: [String]?)] { + func processAccessibleTools(packageGraph: ModulesGraph, fileSystem: FileSystem, environment: BuildEnvironment, for hostTriple: Triple, builtToolHandler: (_ name: String, _ path: RelativePath) throws -> AbsolutePath?) throws -> [String: (path: AbsolutePath, triples: [String]?)] { var pluginAccessibleTools: [String: (path: AbsolutePath, triples: [String]?)] = [:] for dep in try accessibleTools(packageGraph: packageGraph, fileSystem: fileSystem, environment: environment, for: hostTriple) { diff --git a/Sources/SPMTestSupport/MockPackageGraphs.swift b/Sources/SPMTestSupport/MockPackageGraphs.swift index 1d62598796d..4ab9c0cde98 100644 --- a/Sources/SPMTestSupport/MockPackageGraphs.swift +++ b/Sources/SPMTestSupport/MockPackageGraphs.swift @@ -13,7 +13,7 @@ import struct Basics.AbsolutePath import class Basics.ObservabilitySystem import class Basics.ObservabilityScope -import struct PackageGraph.PackageGraph +import struct PackageGraph.ModulesGraph import class PackageModel.Manifest import struct PackageModel.ProductDescription import struct PackageModel.TargetDescription @@ -22,7 +22,7 @@ import class TSCBasic.InMemoryFileSystem @_spi(SwiftPMInternal) public typealias MockPackageGraph = ( - graph: PackageGraph, + graph: ModulesGraph, fileSystem: any FileSystem, observabilityScope: ObservabilityScope ) diff --git a/Sources/SPMTestSupport/MockWorkspace.swift b/Sources/SPMTestSupport/MockWorkspace.swift index 6d21ea9f8d2..344034f4494 100644 --- a/Sources/SPMTestSupport/MockWorkspace.swift +++ b/Sources/SPMTestSupport/MockWorkspace.swift @@ -445,7 +445,7 @@ public final class MockWorkspace { public func checkPackageGraph( roots: [String] = [], deps: [MockDependency], - _ result: (PackageGraph, [Basics.Diagnostic]) -> Void + _ result: (ModulesGraph, [Basics.Diagnostic]) -> Void ) throws { let dependencies = try deps.map { try $0.convert(baseURL: packagesDir, identityResolver: self.identityResolver) } try self.checkPackageGraph(roots: roots, dependencies: dependencies, result) @@ -456,7 +456,7 @@ public final class MockWorkspace { dependencies: [PackageDependency] = [], forceResolvedVersions: Bool = false, expectedSigningEntities: [PackageIdentity: RegistryReleaseMetadata.SigningEntity] = [:], - _ result: (PackageGraph, [Basics.Diagnostic]) throws -> Void + _ result: (ModulesGraph, [Basics.Diagnostic]) throws -> Void ) throws { let observability = ObservabilitySystem.makeForTesting() let rootInput = PackageGraphRootInput( diff --git a/Sources/SPMTestSupport/PackageGraphTester.swift b/Sources/SPMTestSupport/PackageGraphTester.swift index fc4d3c231b5..97aa593a971 100644 --- a/Sources/SPMTestSupport/PackageGraphTester.swift +++ b/Sources/SPMTestSupport/PackageGraphTester.swift @@ -17,14 +17,14 @@ import struct Basics.IdentifiableSet import PackageModel import PackageGraph -public func PackageGraphTester(_ graph: PackageGraph, _ result: (PackageGraphResult) -> Void) { +public func PackageGraphTester(_ graph: ModulesGraph, _ result: (PackageGraphResult) -> Void) { result(PackageGraphResult(graph)) } public final class PackageGraphResult { - public let graph: PackageGraph + public let graph: ModulesGraph - public init(_ graph: PackageGraph) { + public init(_ graph: ModulesGraph) { self.graph = graph } diff --git a/Sources/SPMTestSupport/misc.swift b/Sources/SPMTestSupport/misc.swift index 278ee54bbde..536a0c670c6 100644 --- a/Sources/SPMTestSupport/misc.swift +++ b/Sources/SPMTestSupport/misc.swift @@ -316,6 +316,11 @@ private func swiftArgs( return args } +@available(*, + deprecated, + renamed: "loadModulesGraph", + message: "Rename for consistency: the type of this functions return value is named `ModulesGraph`." +) public func loadPackageGraph( identityResolver: IdentityResolver = DefaultIdentityResolver(), fileSystem: FileSystem, @@ -327,7 +332,33 @@ public func loadPackageGraph( useXCBuildFileRules: Bool = false, customXCTestMinimumDeploymentTargets: [PackageModel.Platform: PlatformVersion]? = .none, observabilityScope: ObservabilityScope -) throws -> PackageGraph { +) throws -> ModulesGraph { + try loadModulesGraph( + identityResolver: identityResolver, + fileSystem: fileSystem, + manifests: manifests, + binaryArtifacts: binaryArtifacts, + explicitProduct: explicitProduct, + shouldCreateMultipleTestProducts: shouldCreateMultipleTestProducts, + createREPLProduct: createREPLProduct, + useXCBuildFileRules: useXCBuildFileRules, + customXCTestMinimumDeploymentTargets: customXCTestMinimumDeploymentTargets, + observabilityScope: observabilityScope + ) +} + +public func loadModulesGraph( + identityResolver: IdentityResolver = DefaultIdentityResolver(), + fileSystem: FileSystem, + manifests: [Manifest], + binaryArtifacts: [PackageIdentity: [String: BinaryArtifact]] = [:], + explicitProduct: String? = .none, + shouldCreateMultipleTestProducts: Bool = false, + createREPLProduct: Bool = false, + useXCBuildFileRules: Bool = false, + customXCTestMinimumDeploymentTargets: [PackageModel.Platform: PlatformVersion]? = .none, + observabilityScope: ObservabilityScope +) throws -> ModulesGraph { let rootManifests = manifests.filter(\.packageKind.isRoot).spm_createDictionary { ($0.path, $0) } let externalManifests = try manifests.filter { !$0.packageKind.isRoot } .reduce( @@ -346,7 +377,7 @@ public func loadPackageGraph( observabilityScope: observabilityScope ) - return try PackageGraph.load( + return try ModulesGraph.load( root: graphRoot, identityResolver: identityResolver, additionalFileRules: useXCBuildFileRules ? FileRuleDescription.xcbuildFileTypes : FileRuleDescription diff --git a/Sources/Workspace/Workspace+Signing.swift b/Sources/Workspace/Workspace+Signing.swift index 1da24b02d0e..dd13d0606b4 100644 --- a/Sources/Workspace/Workspace+Signing.swift +++ b/Sources/Workspace/Workspace+Signing.swift @@ -11,7 +11,7 @@ //===----------------------------------------------------------------------===// import enum PackageFingerprint.FingerprintCheckingMode -import struct PackageGraph.PackageGraph +import struct PackageGraph.ModulesGraph import struct PackageModel.PackageIdentity import struct PackageModel.RegistryReleaseMetadata import enum PackageSigning.SigningEntityCheckingMode @@ -42,7 +42,7 @@ extension SigningEntityCheckingMode { extension Workspace { func validateSignatures( - packageGraph: PackageGraph, + packageGraph: ModulesGraph, expectedSigningEntities: [PackageIdentity: RegistryReleaseMetadata.SigningEntity] ) throws { try expectedSigningEntities.forEach { identity, expectedSigningEntity in diff --git a/Sources/Workspace/Workspace.swift b/Sources/Workspace/Workspace.swift index 3a57b162478..38af1b7362b 100644 --- a/Sources/Workspace/Workspace.swift +++ b/Sources/Workspace/Workspace.swift @@ -880,7 +880,7 @@ extension Workspace { availableLibraries: [LibraryMetadata], expectedSigningEntities: [PackageIdentity: RegistryReleaseMetadata.SigningEntity] = [:], observabilityScope: ObservabilityScope - ) throws -> PackageGraph { + ) throws -> ModulesGraph { let start = DispatchTime.now() self.delegate?.willLoadGraph() defer { @@ -912,7 +912,7 @@ extension Workspace { } // Load the graph. - let packageGraph = try PackageGraph.load( + let packageGraph = try ModulesGraph.load( root: manifests.root, identityResolver: self.identityResolver, additionalFileRules: self.configuration.additionalFileRules, @@ -942,7 +942,7 @@ extension Workspace { rootPath: AbsolutePath, explicitProduct: String? = nil, observabilityScope: ObservabilityScope - ) throws -> PackageGraph { + ) throws -> ModulesGraph { try self.loadPackageGraph( rootInput: PackageGraphRootInput(packages: [rootPath]), explicitProduct: explicitProduct, @@ -1110,7 +1110,7 @@ extension Workspace { } public func loadPluginImports( - packageGraph: PackageGraph + packageGraph: ModulesGraph ) async throws -> [PackageIdentity: [String: [String]]] { let pluginTargets = packageGraph.allTargets.filter { $0.type == .plugin } let scanner = SwiftcImportScanner( @@ -1141,7 +1141,7 @@ extension Workspace { public func loadPackage( with identity: PackageIdentity, - packageGraph: PackageGraph, + packageGraph: ModulesGraph, observabilityScope: ObservabilityScope ) async throws -> Package { try await safe_async { @@ -1154,7 +1154,7 @@ extension Workspace { @available(*, noasync, message: "Use the async alternative") public func loadPackage( with identity: PackageIdentity, - packageGraph: PackageGraph, + packageGraph: ModulesGraph, observabilityScope: ObservabilityScope, completion: @escaping (Result) -> Void ) { @@ -1191,7 +1191,7 @@ extension Workspace { /// Returns `true` if the file at the given path might influence build settings for a `swiftc` or `clang` invocation /// generated by SwiftPM. - public func fileAffectsSwiftOrClangBuildSettings(filePath: AbsolutePath, packageGraph: PackageGraph) -> Bool { + public func fileAffectsSwiftOrClangBuildSettings(filePath: AbsolutePath, packageGraph: ModulesGraph) -> Bool { // TODO: Implement a more sophisticated check that also verifies if the file is in the sources directories of the passed in `packageGraph`. FileRuleDescription.builtinRules.contains { fileRuleDescription in fileRuleDescription.match(path: filePath, toolsVersion: self.currentToolsVersion) diff --git a/Sources/XCBuildSupport/PIFBuilder.swift b/Sources/XCBuildSupport/PIFBuilder.swift index 6907fb9b3bb..44117107238 100644 --- a/Sources/XCBuildSupport/PIFBuilder.swift +++ b/Sources/XCBuildSupport/PIFBuilder.swift @@ -49,7 +49,7 @@ public final class PIFBuilder { public static let allIncludingTestsTargetName = "AllIncludingTests" /// The package graph to build from. - let graph: PackageGraph + let graph: ModulesGraph /// The parameters used to configure the PIF. let parameters: PIFBuilderParameters @@ -69,7 +69,7 @@ public final class PIFBuilder { /// - fileSystem: The file system to read from. /// - observabilityScope: The ObservabilityScope to emit diagnostics to. init( - graph: PackageGraph, + graph: ModulesGraph, parameters: PIFBuilderParameters, fileSystem: FileSystem, observabilityScope: ObservabilityScope @@ -132,7 +132,13 @@ public final class PIFBuilder { } // Convenience method for generating PIF. - public static func generatePIF(buildParameters: BuildParameters, packageGraph: PackageGraph, fileSystem: FileSystem, observabilityScope: ObservabilityScope, preservePIFModelStructure: Bool) throws -> String { + public static func generatePIF( + buildParameters: BuildParameters, + packageGraph: ModulesGraph, + fileSystem: FileSystem, + observabilityScope: ObservabilityScope, + preservePIFModelStructure: Bool + ) throws -> String { let parameters = PIFBuilderParameters(buildParameters) let builder = Self.init( graph: packageGraph, diff --git a/Sources/XCBuildSupport/XcodeBuildSystem.swift b/Sources/XCBuildSupport/XcodeBuildSystem.swift index 89b493e545c..8399a96e0c8 100644 --- a/Sources/XCBuildSupport/XcodeBuildSystem.swift +++ b/Sources/XCBuildSupport/XcodeBuildSystem.swift @@ -28,10 +28,10 @@ import enum TSCUtility.Diagnostics public final class XcodeBuildSystem: SPMBuildCore.BuildSystem { private let buildParameters: BuildParameters - private let packageGraphLoader: () throws -> PackageGraph + private let packageGraphLoader: () throws -> ModulesGraph private let logLevel: Basics.Diagnostic.Severity private let xcbuildPath: AbsolutePath - private var packageGraph: PackageGraph? + private var packageGraph: ModulesGraph? private var pifBuilder: PIFBuilder? private let fileSystem: FileSystem private let observabilityScope: ObservabilityScope @@ -77,7 +77,7 @@ public final class XcodeBuildSystem: SPMBuildCore.BuildSystem { public init( buildParameters: BuildParameters, - packageGraphLoader: @escaping () throws -> PackageGraph, + packageGraphLoader: @escaping () throws -> ModulesGraph, outputStream: OutputByteStream, logLevel: Basics.Diagnostic.Severity, fileSystem: FileSystem, @@ -278,7 +278,7 @@ public final class XcodeBuildSystem: SPMBuildCore.BuildSystem { /// Returns the package graph using the graph loader closure. /// /// First access will cache the graph. - public func getPackageGraph() throws -> PackageGraph { + public func getPackageGraph() throws -> ModulesGraph { try memoize(to: &packageGraph) { try packageGraphLoader() } diff --git a/Sources/swift-bootstrap/main.swift b/Sources/swift-bootstrap/main.swift index f29942cb356..6895b9c6b42 100644 --- a/Sources/swift-bootstrap/main.swift +++ b/Sources/swift-bootstrap/main.swift @@ -353,7 +353,7 @@ struct SwiftBootstrapBuildTool: ParsableCommand { ) } - func loadPackageGraph(packagePath: AbsolutePath, manifestLoader: ManifestLoader) throws -> PackageGraph { + func loadPackageGraph(packagePath: AbsolutePath, manifestLoader: ManifestLoader) throws -> ModulesGraph { let rootPackageRef = PackageReference(identity: .init(path: packagePath), kind: .root(packagePath)) let rootPackageManifest = try temp_await { self.loadManifest(manifestLoader: manifestLoader, package: rootPackageRef, completion: $0) } @@ -380,7 +380,7 @@ struct SwiftBootstrapBuildTool: ParsableCommand { observabilityScope: observabilityScope ) - return try PackageGraph.load( + return try ModulesGraph.load( root: packageGraphRoot, identityResolver: identityResolver, externalManifests: loadedManifests.reduce(into: OrderedCollections.OrderedDictionary()) { partial, item in diff --git a/Tests/BuildTests/BuildPlanTests.swift b/Tests/BuildTests/BuildPlanTests.swift index 6bf75fdc170..0ab7adc3bd6 100644 --- a/Tests/BuildTests/BuildPlanTests.swift +++ b/Tests/BuildTests/BuildPlanTests.swift @@ -39,7 +39,7 @@ extension Build.BuildPlan { /// testing purposes only. convenience init( buildParameters: BuildParameters, - graph: PackageGraph, + graph: ModulesGraph, additionalFileRules: [FileRuleDescription] = [], buildToolPluginInvocationResults: [ResolvedTarget.ID: [BuildToolPluginInvocationResult]] = [:], prebuildCommandResults: [ResolvedTarget.ID: [PrebuildCommandResult]] = [:], @@ -76,7 +76,7 @@ final class BuildPlanTests: XCTestCase { let observability = ObservabilitySystem.makeForTesting() let fooPkg: AbsolutePath = "/fooPkg" let barPkg: AbsolutePath = "/barPkg" - XCTAssertThrowsError(try loadPackageGraph( + XCTAssertThrowsError(try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -134,7 +134,7 @@ final class BuildPlanTests: XCTestCase { "/barPkg/Sources/BarLogging/file.swift" ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -207,7 +207,7 @@ final class BuildPlanTests: XCTestCase { "/yPkg/Sources/YUtils/file.swift" ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -331,7 +331,7 @@ final class BuildPlanTests: XCTestCase { "/bazPkg/Sources/BazLogging/file.swift" ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -421,7 +421,7 @@ final class BuildPlanTests: XCTestCase { "/barPkg/Sources/BarLogging/file.swift" ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -498,7 +498,7 @@ final class BuildPlanTests: XCTestCase { let fooPkg: AbsolutePath = "/fooPkg" let barPkg: AbsolutePath = "/barPkg" - XCTAssertThrowsError(try loadPackageGraph( + XCTAssertThrowsError(try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -556,7 +556,7 @@ final class BuildPlanTests: XCTestCase { "/barPkg/Sources/BarLogging/file.swift" ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -702,7 +702,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -866,7 +866,7 @@ final class BuildPlanTests: XCTestCase { // Plan package build with explicit module build let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -934,7 +934,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -1081,7 +1081,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fileSystem, manifests: [ Manifest.createRootManifest( @@ -1139,7 +1139,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -1229,7 +1229,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -1324,7 +1324,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -1489,7 +1489,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -1581,7 +1581,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -1696,7 +1696,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -1823,7 +1823,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -1871,7 +1871,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -1939,7 +1939,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -2072,7 +2072,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -2318,7 +2318,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -2429,7 +2429,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -2533,7 +2533,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -2587,7 +2587,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let g = try loadPackageGraph( + let g = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -2730,7 +2730,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -2853,7 +2853,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -3027,7 +3027,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fileSystem, manifests: [ Manifest.createRootManifest( @@ -3117,7 +3117,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fileSystem, manifests: [ Manifest.createRootManifest( @@ -3235,7 +3235,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -3265,7 +3265,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fileSystem, manifests: [ Manifest.createRootManifest( @@ -3312,7 +3312,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fileSystem, manifests: [ Manifest.createRootManifest( @@ -3356,7 +3356,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -3438,7 +3438,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -3490,7 +3490,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -3543,7 +3543,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fileSystem, manifests: [ Manifest.createRootManifest( @@ -3615,7 +3615,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fileSystem, manifests: [ Manifest.createRootManifest( @@ -3687,7 +3687,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fileSystem, manifests: [ Manifest.createRootManifest( @@ -3766,7 +3766,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fileSystem, manifests: [ Manifest.createRootManifest( @@ -3949,7 +3949,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [aManifest, bManifest], observabilityScope: observability.topScope @@ -4215,7 +4215,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [aManifest], observabilityScope: observability.topScope @@ -4254,7 +4254,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -4388,7 +4388,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fileSystem, manifests: [ Manifest.createRootManifest( @@ -4555,7 +4555,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fileSystem, manifests: [ Manifest.createRootManifest( @@ -4623,7 +4623,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -4707,7 +4707,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -4795,7 +4795,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -4905,7 +4905,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -5020,7 +5020,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -5103,7 +5103,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -5246,7 +5246,7 @@ final class BuildPlanTests: XCTestCase { let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -5314,7 +5314,7 @@ final class BuildPlanTests: XCTestCase { let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -5382,7 +5382,7 @@ final class BuildPlanTests: XCTestCase { let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -5456,7 +5456,7 @@ final class BuildPlanTests: XCTestCase { let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -5568,7 +5568,7 @@ final class BuildPlanTests: XCTestCase { let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -5703,7 +5703,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -5791,7 +5791,7 @@ final class BuildPlanTests: XCTestCase { ) let buildPath = AbsolutePath("/Pkg/.build") let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -5847,7 +5847,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -5901,7 +5901,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fileSystem, manifests: [ Manifest.createRootManifest( @@ -5958,7 +5958,7 @@ final class BuildPlanTests: XCTestCase { let fs = InMemoryFileSystem(emptyFiles: "/Pkg/Sources/exe/main.swift", "/ExtPkg/Sources/ExtLib/best.swift") let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -6019,7 +6019,7 @@ final class BuildPlanTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -6100,7 +6100,7 @@ final class BuildPlanTests: XCTestCase { "/barPkg/Sources/BarLogging/file.swift" ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( diff --git a/Tests/BuildTests/LLBuildManifestBuilderTests.swift b/Tests/BuildTests/LLBuildManifestBuilderTests.swift index cb4b8f6d9e9..f66e0dc5e25 100644 --- a/Tests/BuildTests/LLBuildManifestBuilderTests.swift +++ b/Tests/BuildTests/LLBuildManifestBuilderTests.swift @@ -32,7 +32,7 @@ final class LLBuildManifestBuilderTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( diff --git a/Tests/BuildTests/ModuleAliasingBuildTests.swift b/Tests/BuildTests/ModuleAliasingBuildTests.swift index 090dedb431a..23244d40a5a 100644 --- a/Tests/BuildTests/ModuleAliasingBuildTests.swift +++ b/Tests/BuildTests/ModuleAliasingBuildTests.swift @@ -33,7 +33,7 @@ final class ModuleAliasingBuildTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let _ = try loadPackageGraph( + let _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -83,7 +83,7 @@ final class ModuleAliasingBuildTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let _ = try loadPackageGraph( + let _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -135,7 +135,7 @@ final class ModuleAliasingBuildTests: XCTestCase { "/barPkg/Sources/Logging/fileLogging.swift" ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -214,7 +214,7 @@ final class ModuleAliasingBuildTests: XCTestCase { "/barPkg/Sources/Logging/fileLogging.swift" ) let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadPackageGraph( + XCTAssertThrowsError(try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -276,7 +276,7 @@ final class ModuleAliasingBuildTests: XCTestCase { "/barPkg/Sources/Logging/fileLogging.swift" ) let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadPackageGraph( + XCTAssertThrowsError(try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -337,7 +337,7 @@ final class ModuleAliasingBuildTests: XCTestCase { "/bazPkg/Sources/Logging/fileLogging.swift" ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -421,7 +421,7 @@ final class ModuleAliasingBuildTests: XCTestCase { "/bazPkg/Sources/Logging/fileLogging.swift" ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -507,7 +507,7 @@ final class ModuleAliasingBuildTests: XCTestCase { "/yPkg/Sources/Logging/fileLogging.swift" ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -638,7 +638,7 @@ final class ModuleAliasingBuildTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -767,7 +767,7 @@ final class ModuleAliasingBuildTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -873,7 +873,7 @@ final class ModuleAliasingBuildTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadPackageGraph( + XCTAssertThrowsError(try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -936,7 +936,7 @@ final class ModuleAliasingBuildTests: XCTestCase { "/swift-metrics/Sources/Logging/fileLogging.swift" ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -1017,7 +1017,7 @@ final class ModuleAliasingBuildTests: XCTestCase { "/fooPkg/Sources/Logging/include/fileLogging.h" ) let observability = ObservabilitySystem.makeForTesting() - let _ = try loadPackageGraph( + let _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -1073,7 +1073,7 @@ final class ModuleAliasingBuildTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let _ = try loadPackageGraph( + let _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -1145,7 +1145,7 @@ final class ModuleAliasingBuildTests: XCTestCase { "/fooPkg/Sources/Perf/include/filePerf.h" ) let observability = ObservabilitySystem.makeForTesting() - XCTAssertNoThrow(try loadPackageGraph( + XCTAssertNoThrow(try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -1198,7 +1198,7 @@ final class ModuleAliasingBuildTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - XCTAssertNoThrow(try loadPackageGraph( + XCTAssertNoThrow(try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -1262,7 +1262,7 @@ final class ModuleAliasingBuildTests: XCTestCase { "/barPkg/Sources/Logging/fileLogging.swift" ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -1351,7 +1351,7 @@ final class ModuleAliasingBuildTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -1471,7 +1471,7 @@ final class ModuleAliasingBuildTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -1683,7 +1683,7 @@ final class ModuleAliasingBuildTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -1884,7 +1884,7 @@ final class ModuleAliasingBuildTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -2094,7 +2094,7 @@ final class ModuleAliasingBuildTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -2308,7 +2308,7 @@ final class ModuleAliasingBuildTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -2458,7 +2458,7 @@ final class ModuleAliasingBuildTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -2595,7 +2595,7 @@ final class ModuleAliasingBuildTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -2741,7 +2741,7 @@ final class ModuleAliasingBuildTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -2893,7 +2893,7 @@ final class ModuleAliasingBuildTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -3044,7 +3044,7 @@ final class ModuleAliasingBuildTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -3144,7 +3144,7 @@ final class ModuleAliasingBuildTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -3257,7 +3257,7 @@ final class ModuleAliasingBuildTests: XCTestCase { "/xPkg/Sources/Utils/file.swift" ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -3325,7 +3325,7 @@ final class ModuleAliasingBuildTests: XCTestCase { "/cPkg/Sources/Utils/file.swift" ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -3484,7 +3484,7 @@ final class ModuleAliasingBuildTests: XCTestCase { "/zPkg/Sources/Utils/file.swift" ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -3655,7 +3655,7 @@ final class ModuleAliasingBuildTests: XCTestCase { "/zPkg/Sources/Utils/file.swift" ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -3783,7 +3783,7 @@ final class ModuleAliasingBuildTests: XCTestCase { "/zPkg/Sources/Utils/file.swift" ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -3912,7 +3912,7 @@ final class ModuleAliasingBuildTests: XCTestCase { "/yPkg/Sources/Utils/file.swift" ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -4070,7 +4070,7 @@ final class ModuleAliasingBuildTests: XCTestCase { "/zPkg/Sources/Utils/file.swift" ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -4236,7 +4236,7 @@ final class ModuleAliasingBuildTests: XCTestCase { "/zPkg/Sources/Utils/file.swift" ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -4411,7 +4411,7 @@ final class ModuleAliasingBuildTests: XCTestCase { "/aPkg/Sources/Utils/file.swift" ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -4568,7 +4568,7 @@ final class ModuleAliasingBuildTests: XCTestCase { "/Other/Sources/Other/file.swift" ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -4638,7 +4638,7 @@ final class ModuleAliasingBuildTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() do { - _ = try loadPackageGraph( + _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( diff --git a/Tests/BuildTests/ProductBuildDescriptionTests.swift b/Tests/BuildTests/ProductBuildDescriptionTests.swift index 932c1c56be9..f07646f64a5 100644 --- a/Tests/BuildTests/ProductBuildDescriptionTests.swift +++ b/Tests/BuildTests/ProductBuildDescriptionTests.swift @@ -22,7 +22,7 @@ import struct PackageModel.TargetDescription @testable import struct PackageGraph.ResolvedProduct -import func SPMTestSupport.loadPackageGraph +import func SPMTestSupport.loadModulesGraph import func SPMTestSupport.mockBuildParameters import func SPMTestSupport.XCTAssertNoDiagnostics import XCTest @@ -35,7 +35,7 @@ final class ProductBuildDescriptionTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( diff --git a/Tests/PackageGraphPerformanceTests/PackageGraphPerfTests.swift b/Tests/PackageGraphPerformanceTests/PackageGraphPerfTests.swift index facd1f603d2..622eeb68a83 100644 --- a/Tests/PackageGraphPerformanceTests/PackageGraphPerfTests.swift +++ b/Tests/PackageGraphPerformanceTests/PackageGraphPerfTests.swift @@ -85,7 +85,7 @@ final class PackageGraphPerfTests: XCTestCasePerf { measure { let observability = ObservabilitySystem.makeForTesting() - let g = try! PackageGraph.load( + let g = try! ModulesGraph.load( root: PackageGraphRoot( input: PackageGraphRootInput(packages: [rootManifest.path]), manifests: [rootManifest.path: rootManifest], diff --git a/Tests/PackageGraphTests/PackageGraphTests.swift b/Tests/PackageGraphTests/ModulesGraphTests.swift similarity index 97% rename from Tests/PackageGraphTests/PackageGraphTests.swift rename to Tests/PackageGraphTests/ModulesGraphTests.swift index ab86f67d6a8..efae57d71a4 100644 --- a/Tests/PackageGraphTests/PackageGraphTests.swift +++ b/Tests/PackageGraphTests/ModulesGraphTests.swift @@ -19,7 +19,7 @@ import XCTest import struct TSCBasic.ByteString import class TSCBasic.InMemoryFileSystem -final class PackageGraphTests: XCTestCase { +final class ModulesGraphTests: XCTestCase { func testBasic() throws { let fs = InMemoryFileSystem(emptyFiles: "/Foo/Sources/Foo/source.swift", @@ -31,7 +31,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let g = try loadPackageGraph( + let g = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -98,7 +98,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let g = try loadPackageGraph( + let g = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -142,7 +142,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph( + _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -195,7 +195,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph( + _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -227,7 +227,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let g = try loadPackageGraph( + let g = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -271,7 +271,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let g = try loadPackageGraph( + let g = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -311,7 +311,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph( + _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -349,7 +349,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph( + _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -412,7 +412,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph( + _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -480,7 +480,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph( + _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createFileSystemManifest( @@ -547,7 +547,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph( + _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -597,7 +597,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph( + _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -647,7 +647,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph( + _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -692,7 +692,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph( + _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -735,7 +735,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let g = try loadPackageGraph( + let g = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -764,7 +764,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph( + _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -792,7 +792,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph( + _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -826,7 +826,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph( + _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -860,7 +860,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph( + _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -891,7 +891,7 @@ final class PackageGraphTests: XCTestCase { "/XYZ/Tests/XYZTests/tests.swift" ) let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph( + _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -915,7 +915,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph( + _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -940,7 +940,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph( + _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -969,7 +969,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph( + _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -1001,7 +1001,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph( + _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -1078,7 +1078,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph( + _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -1117,7 +1117,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph( + _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -1179,7 +1179,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph( + _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -1213,7 +1213,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph( + _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -1269,7 +1269,7 @@ final class PackageGraphTests: XCTestCase { let bazPkg: AbsolutePath = "/Baz" let observability = ObservabilitySystem.makeForTesting() - XCTAssertThrowsError(try loadPackageGraph( + XCTAssertThrowsError(try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -1320,7 +1320,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph( + _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -1392,7 +1392,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -1474,7 +1474,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph( + _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -1595,7 +1595,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph( + _ = try loadModulesGraph( fileSystem: fileSystem, manifests: [ Manifest.createRootManifest( @@ -1681,7 +1681,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph( + _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -1718,7 +1718,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph( + _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -1762,7 +1762,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph( + _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -1799,7 +1799,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph( + _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -1867,7 +1867,7 @@ final class PackageGraphTests: XCTestCase { do { let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph(fileSystem: fs, manifests: manifests, observabilityScope: observability.topScope) + _ = try loadModulesGraph(fileSystem: fs, manifests: manifests, observabilityScope: observability.topScope) testDiagnostics(observability.diagnostics) { result in result.check( diagnostic: """ @@ -1889,7 +1889,7 @@ final class PackageGraphTests: XCTestCase { ] let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph(fileSystem: fs, manifests: fixedManifests, observabilityScope: observability.topScope) + _ = try loadModulesGraph(fileSystem: fs, manifests: fixedManifests, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) } } @@ -1927,7 +1927,7 @@ final class PackageGraphTests: XCTestCase { do { let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph(fileSystem: fs, manifests: manifests, observabilityScope: observability.topScope) + _ = try loadModulesGraph(fileSystem: fs, manifests: manifests, observabilityScope: observability.topScope) testDiagnostics(observability.diagnostics) { result in result.check( diagnostic: """ @@ -1950,7 +1950,7 @@ final class PackageGraphTests: XCTestCase { ] let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph(fileSystem: fs, manifests: fixedManifests, observabilityScope: observability.topScope) + _ = try loadModulesGraph(fileSystem: fs, manifests: fixedManifests, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) } } @@ -1986,7 +1986,7 @@ final class PackageGraphTests: XCTestCase { do { let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph(fileSystem: fs, manifests: manifests, observabilityScope: observability.topScope) + _ = try loadModulesGraph(fileSystem: fs, manifests: manifests, observabilityScope: observability.topScope) testDiagnostics(observability.diagnostics) { result in result.check( diagnostic: """ @@ -2008,7 +2008,7 @@ final class PackageGraphTests: XCTestCase { ] let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph(fileSystem: fs, manifests: fixedManifests, observabilityScope: observability.topScope) + _ = try loadModulesGraph(fileSystem: fs, manifests: fixedManifests, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) } } @@ -2044,7 +2044,7 @@ final class PackageGraphTests: XCTestCase { do { let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph(fileSystem: fs, manifests: manifests, observabilityScope: observability.topScope) + _ = try loadModulesGraph(fileSystem: fs, manifests: manifests, observabilityScope: observability.topScope) testDiagnostics(observability.diagnostics) { result in let diagnostic = result.check( diagnostic: """ @@ -2067,7 +2067,7 @@ final class PackageGraphTests: XCTestCase { ] let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph(fileSystem: fs, manifests: fixedManifests, observabilityScope: observability.topScope) + _ = try loadModulesGraph(fileSystem: fs, manifests: fixedManifests, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) } } @@ -2104,7 +2104,7 @@ final class PackageGraphTests: XCTestCase { ] let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph(fileSystem: fs, manifests: manifests, observabilityScope: observability.topScope) + _ = try loadModulesGraph(fileSystem: fs, manifests: manifests, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) } @@ -2141,7 +2141,7 @@ final class PackageGraphTests: XCTestCase { do { let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph(fileSystem: fs, manifests: manifests, observabilityScope: observability.topScope) + _ = try loadModulesGraph(fileSystem: fs, manifests: manifests, observabilityScope: observability.topScope) testDiagnostics(observability.diagnostics) { result in let diagnostic = result.check( diagnostic: """ @@ -2164,7 +2164,7 @@ final class PackageGraphTests: XCTestCase { ] let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph(fileSystem: fs, manifests: fixedManifests, observabilityScope: observability.topScope) + _ = try loadModulesGraph(fileSystem: fs, manifests: fixedManifests, observabilityScope: observability.topScope) XCTAssertNoDiagnostics(observability.diagnostics) } } @@ -2276,7 +2276,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [manifest], customXCTestMinimumDeploymentTargets: customXCTestMinimumDeploymentTargets, @@ -2372,7 +2372,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [manifest], observabilityScope: observability.topScope @@ -2432,7 +2432,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [manifest], customXCTestMinimumDeploymentTargets: customXCTestMinimumDeploymentTargets, @@ -2505,7 +2505,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [manifest], observabilityScope: observability.topScope @@ -2548,7 +2548,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [manifest], observabilityScope: observability.topScope @@ -2588,7 +2588,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph( + _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -2647,7 +2647,7 @@ final class PackageGraphTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - _ = try loadPackageGraph( + _ = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( diff --git a/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift b/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift index 18cbcd13d14..d42a55db9c4 100644 --- a/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift +++ b/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift @@ -57,7 +57,7 @@ class SourceKitLSPAPITests: XCTestCase { } extension SourceKitLSPAPI.BuildDescription { - @discardableResult func checkArguments(for targetName: String, graph: PackageGraph, partialArguments: [String]) throws -> Bool { + @discardableResult func checkArguments(for targetName: String, graph: ModulesGraph, partialArguments: [String]) throws -> Bool { let target = try XCTUnwrap(graph.allTargets.first(where: { $0.name == targetName })) let buildTarget = try XCTUnwrap(self.getBuildTarget(for: target)) diff --git a/Tests/XCBuildSupportTests/PIFBuilderTests.swift b/Tests/XCBuildSupportTests/PIFBuilderTests.swift index dcabed69073..79fc28be3b5 100644 --- a/Tests/XCBuildSupportTests/PIFBuilderTests.swift +++ b/Tests/XCBuildSupportTests/PIFBuilderTests.swift @@ -42,7 +42,7 @@ class PIFBuilderTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createLocalSourceControlManifest( @@ -113,7 +113,7 @@ class PIFBuilderTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createManifest( @@ -400,7 +400,7 @@ class PIFBuilderTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -734,7 +734,7 @@ class PIFBuilderTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -986,7 +986,7 @@ class PIFBuilderTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -1188,7 +1188,7 @@ class PIFBuilderTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -1486,7 +1486,7 @@ class PIFBuilderTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -1709,7 +1709,7 @@ class PIFBuilderTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -1762,7 +1762,7 @@ class PIFBuilderTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createManifest( @@ -1820,7 +1820,7 @@ class PIFBuilderTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -1939,7 +1939,7 @@ class PIFBuilderTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -2013,7 +2013,7 @@ class PIFBuilderTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -2229,7 +2229,7 @@ class PIFBuilderTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -2443,7 +2443,7 @@ class PIFBuilderTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createManifest( @@ -2512,7 +2512,7 @@ class PIFBuilderTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -2562,7 +2562,7 @@ class PIFBuilderTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( From 31bc8087b42e8d2a586bfc508594103ce47dde7d Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Sat, 24 Feb 2024 01:17:37 +0000 Subject: [PATCH 024/159] Fix `package` access modifier in XCBuild support (#7258) `package` access modifier was previously not supported in `swift build --build-system xcode`. This causes build issues when attempting to produce Universal binaries for SwiftPM, for example in this job https://ci.swift.org/job/swift-PR-source-compat-suite-debug-macos/1297/consoleFull. The reason was that `-package-name` option was not added to `OTHER_SWIFT_FLAGS` in `PIFBuilder.swift`. Additionally, in llbuild support code we were shelling out to Swift Driver for every target to check whether `-package-name` is supported. Now with `-package-name` options calculation generalized across build systems, Swift Driver checks are done once per `BuildParameters` initialization, which reduces excessive shelling for llbuild. Resolves rdar://120925895. --- Package.swift | 4 +- .../SwiftTargetBuildDescription.swift | 36 ++++---- Sources/CoreCommands/SwiftTool.swift | 13 ++- .../DriverSupport/DriverSupportUtils.swift | 11 ++- Sources/PackageModel/Target/SwiftTarget.swift | 2 +- .../BuildParameters+Driver.swift | 13 ++- Sources/SPMBuildCore/CMakeLists.txt | 1 + .../ResolvedPackage+Extensions.swift | 25 ++++++ Sources/SPMTestSupport/PIFTester.swift | 82 +++++++++++++++---- Sources/SPMTestSupport/SwiftPMProduct.swift | 2 +- Sources/XCBuildSupport/PIFBuilder.swift | 42 ++++++++-- Sources/XCBuildSupport/XcodeBuildSystem.swift | 5 +- Sources/swift-bootstrap/CMakeLists.txt | 1 + Sources/swift-bootstrap/main.swift | 13 ++- Tests/BuildTests/BuildPlanTests.swift | 37 ++++++++- Tests/CommandsTests/APIDiffTests.swift | 3 + .../XCBuildSupportTests/PIFBuilderTests.swift | 77 +++++++++++------ 17 files changed, 284 insertions(+), 83 deletions(-) create mode 100644 Sources/SPMBuildCore/ResolvedPackage+Extensions.swift diff --git a/Package.swift b/Package.swift index 57b9d328950..1b67941a013 100644 --- a/Package.swift +++ b/Package.swift @@ -4,7 +4,7 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2014-2023 Apple Inc. and the Swift project authors +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information @@ -349,7 +349,7 @@ let package = Package( .target( /** Support for building using Xcode's build system */ name: "XCBuildSupport", - dependencies: ["SPMBuildCore", "PackageGraph"], + dependencies: ["DriverSupport", "SPMBuildCore", "PackageGraph"], exclude: ["CMakeLists.txt", "CODEOWNERS"] ), .target( diff --git a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift index 206bf6b163c..184ab64f59b 100644 --- a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift @@ -11,10 +11,13 @@ //===----------------------------------------------------------------------===// import Basics + import Foundation import PackageGraph import PackageLoading import PackageModel + +@_spi(SwiftPMInternal) import SPMBuildCore #if USE_IMPL_ONLY_IMPORTS @@ -419,18 +422,6 @@ public final class SwiftTargetBuildDescription { try self.fileSystem.writeIfChanged(path: path, string: content) } - private func packageNameArgumentIfSupported(with pkg: ResolvedPackage, packageAccess: Bool) -> [String] { - let flag = "-package-name" - if pkg.manifest.usePackageNameFlag, - DriverSupport.checkToolchainDriverFlags(flags: [flag], toolchain: self.buildParameters.toolchain, fileSystem: self.fileSystem) { - if packageAccess { - let pkgID = pkg.identity.description.spm_mangledToC99ExtendedIdentifier() - return [flag, pkgID] - } - } - return [] - } - private func macroArguments() throws -> [String] { var args = [String]() @@ -450,7 +441,14 @@ public final class SwiftTargetBuildDescription { #endif // If we're using an OSS toolchain, add the required arguments bringing in the plugin server from the default toolchain if available. - if self.buildParameters.toolchain.isSwiftDevelopmentToolchain, DriverSupport.checkSupportedFrontendFlags(flags: ["-external-plugin-path"], toolchain: self.buildParameters.toolchain, fileSystem: self.fileSystem), let pluginServer = try self.buildParameters.toolchain.swiftPluginServerPath { + if self.buildParameters.toolchain.isSwiftDevelopmentToolchain, + DriverSupport.checkSupportedFrontendFlags( + flags: ["-external-plugin-path"], + toolchain: self.buildParameters.toolchain, + fileSystem: self.fileSystem + ), + let pluginServer = try self.buildParameters.toolchain.swiftPluginServerPath + { let toolchainUsrPath = pluginServer.parentDirectory.parentDirectory let pluginPathComponents = ["lib", "swift", "host", "plugins"] @@ -631,7 +629,10 @@ public final class SwiftTargetBuildDescription { args += ["-user-module-version", version.description] } - args += self.packageNameArgumentIfSupported(with: self.package, packageAccess: self.target.packageAccess) + args += self.package.packageNameArgument( + target: self.target, + isPackageNameSupported: self.buildParameters.driverParameters.isPackageAccessModifierSupported + ) args += try self.macroArguments() // rdar://117578677 @@ -656,7 +657,12 @@ public final class SwiftTargetBuildDescription { result.append("-module-name") result.append(self.target.c99name) - result.append(contentsOf: packageNameArgumentIfSupported(with: self.package, packageAccess: self.target.packageAccess)) + result.append( + contentsOf: self.package.packageNameArgument( + target: self.target, + isPackageNameSupported: self.buildParameters.driverParameters.isPackageAccessModifierSupported + ) + ) if !scanInvocation { result.append("-emit-dependencies") diff --git a/Sources/CoreCommands/SwiftTool.swift b/Sources/CoreCommands/SwiftTool.swift index 478dbc253de..377eff2451c 100644 --- a/Sources/CoreCommands/SwiftTool.swift +++ b/Sources/CoreCommands/SwiftTool.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2014-2023 Apple Inc. and the Swift project authors +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information @@ -23,8 +23,11 @@ import SPMBuildCore import Workspace #if USE_IMPL_ONLY_IMPORTS -@_implementationOnly import DriverSupport +@_implementationOnly +@_spi(SwiftPMInternal) +import DriverSupport #else +@_spi(SwiftPMInternal) import DriverSupport #endif @@ -757,7 +760,11 @@ public final class SwiftTool { enableParseableModuleInterfaces: options.build.shouldEnableParseableModuleInterfaces, explicitTargetDependencyImportCheckingMode: options.build.explicitTargetDependencyImportCheck.modeParameter, useIntegratedSwiftDriver: options.build.useIntegratedSwiftDriver, - useExplicitModuleBuild: options.build.useExplicitModuleBuild + useExplicitModuleBuild: options.build.useExplicitModuleBuild, + isPackageAccessModifierSupported: DriverSupport.isPackageNameSupported( + toolchain: toolchain, + fileSystem: self.fileSystem + ) ), linkingParameters: .init( linkerDeadStrip: options.linker.linkerDeadStrip, diff --git a/Sources/DriverSupport/DriverSupportUtils.swift b/Sources/DriverSupport/DriverSupportUtils.swift index 9e5bf00a739..7f8452ecc10 100644 --- a/Sources/DriverSupport/DriverSupportUtils.swift +++ b/Sources/DriverSupport/DriverSupportUtils.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2022-2024 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information @@ -20,7 +20,7 @@ import struct TSCBasic.ProcessResult public enum DriverSupport { private static var flagsMap = ThreadSafeBox<[String: Set]>() - // This checks _frontend_ supported flags, which are not necessarily supported in the driver. + /// This checks _frontend_ supported flags, which are not necessarily supported in the driver. public static func checkSupportedFrontendFlags( flags: Set, toolchain: PackageModel.Toolchain, @@ -54,7 +54,7 @@ public enum DriverSupport { // This checks if given flags are supported in the built-in toolchain driver. Currently // there's no good way to get the supported flags from it, so run `swiftc -h` directly // to get the flags and cache the result. - public static func checkToolchainDriverFlags( + static func checkToolchainDriverFlags( flags: Set, toolchain: PackageModel.Toolchain, fileSystem: FileSystem @@ -83,4 +83,9 @@ public enum DriverSupport { return false } } + + @_spi(SwiftPMInternal) + public static func isPackageNameSupported(toolchain: PackageModel.Toolchain, fileSystem: FileSystem) -> Bool { + DriverSupport.checkToolchainDriverFlags(flags: ["-package-name"], toolchain: toolchain, fileSystem: fileSystem) + } } diff --git a/Sources/PackageModel/Target/SwiftTarget.swift b/Sources/PackageModel/Target/SwiftTarget.swift index 13e16d5b30e..0dd16dbed91 100644 --- a/Sources/PackageModel/Target/SwiftTarget.swift +++ b/Sources/PackageModel/Target/SwiftTarget.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2014-2023 Apple Inc. and the Swift project authors +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information diff --git a/Sources/SPMBuildCore/BuildParameters/BuildParameters+Driver.swift b/Sources/SPMBuildCore/BuildParameters/BuildParameters+Driver.swift index 6007d1d6046..0355f182cfb 100644 --- a/Sources/SPMBuildCore/BuildParameters/BuildParameters+Driver.swift +++ b/Sources/SPMBuildCore/BuildParameters/BuildParameters+Driver.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2020-2023 Apple Inc. and the Swift project authors +// Copyright (c) 2020-2024 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information @@ -25,13 +25,15 @@ extension BuildParameters { enableParseableModuleInterfaces: Bool = false, explicitTargetDependencyImportCheckingMode: TargetDependencyImportCheckingMode = .none, useIntegratedSwiftDriver: Bool = false, - useExplicitModuleBuild: Bool = false + useExplicitModuleBuild: Bool = false, + isPackageAccessModifierSupported: Bool = false ) { self.canRenameEntrypointFunctionName = canRenameEntrypointFunctionName self.enableParseableModuleInterfaces = enableParseableModuleInterfaces self.explicitTargetDependencyImportCheckingMode = explicitTargetDependencyImportCheckingMode self.useIntegratedSwiftDriver = useIntegratedSwiftDriver self.useExplicitModuleBuild = useExplicitModuleBuild + self.isPackageAccessModifierSupported = isPackageAccessModifierSupported } /// Whether to enable the entry-point-function-name feature. @@ -45,11 +47,16 @@ extension BuildParameters { /// `.swiftmodule`s. public var enableParseableModuleInterfaces: Bool - /// Whether to use the integrated Swift driver rather than shelling out + /// Whether to use the integrated Swift Driver rather than shelling out /// to a separate process. public var useIntegratedSwiftDriver: Bool /// Whether to use the explicit module build flow (with the integrated driver). public var useExplicitModuleBuild: Bool + + /// Whether the version of Swift Driver used in the currently selected toolchain + /// supports `-package-name` options. + @_spi(SwiftPMInternal) + public var isPackageAccessModifierSupported: Bool } } diff --git a/Sources/SPMBuildCore/CMakeLists.txt b/Sources/SPMBuildCore/CMakeLists.txt index d5c3ee43344..ed5d5d5d828 100644 --- a/Sources/SPMBuildCore/CMakeLists.txt +++ b/Sources/SPMBuildCore/CMakeLists.txt @@ -23,6 +23,7 @@ add_library(SPMBuildCore Plugins/PluginMessages.swift Plugins/PluginScriptRunner.swift PrebuildCommandResult.swift + ResolvedPackage+Extensions.swift Triple+Extensions.swift XCFrameworkMetadata.swift) # NOTE(compnerd) workaround for CMake not setting up include flags yet diff --git a/Sources/SPMBuildCore/ResolvedPackage+Extensions.swift b/Sources/SPMBuildCore/ResolvedPackage+Extensions.swift new file mode 100644 index 00000000000..f4e433b8631 --- /dev/null +++ b/Sources/SPMBuildCore/ResolvedPackage+Extensions.swift @@ -0,0 +1,25 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import struct PackageGraph.ResolvedPackage +import struct PackageGraph.ResolvedTarget + +extension ResolvedPackage { + @_spi(SwiftPMInternal) + public func packageNameArgument(target: ResolvedTarget, isPackageNameSupported: Bool) -> [String] { + if self.manifest.usePackageNameFlag, target.packageAccess { + ["-package-name", self.identity.description.spm_mangledToC99ExtendedIdentifier()] + } else { + [] + } + } +} diff --git a/Sources/SPMTestSupport/PIFTester.swift b/Sources/SPMTestSupport/PIFTester.swift index 4e4641fa016..85b64cbf6e5 100644 --- a/Sources/SPMTestSupport/PIFTester.swift +++ b/Sources/SPMTestSupport/PIFTester.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2014-2020 Apple Inc. and the Swift project authors +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information @@ -32,7 +32,12 @@ public final class PIFWorkspaceTester { targetMap = Dictionary(uniqueKeysWithValues: targetsByGUID) } - public func checkProject(_ guid: PIF.GUID, file: StaticString = #file, line: UInt = #line, body: (PIFProjectTester) -> Void) throws { + public func checkProject( + _ guid: PIF.GUID, + file: StaticString = #file, + line: UInt = #line, + body: (PIFProjectTester) -> Void + ) throws { guard let project = projectMap[guid] else { return XCTFail("project \(guid) not found", file: file, line: line) } @@ -55,10 +60,20 @@ public final class PIFProjectTester { fileprivate init(project: PIF.Project, targetMap: [PIF.GUID: PIF.BaseTarget]) throws { self.project = project self.targetMap = targetMap - self.fileMap = try collectFiles(from: project.groupTree, parentPath: project.path, projectPath: project.path, builtProductsPath: project.path) + self.fileMap = try collectFiles( + from: project.groupTree, + parentPath: project.path, + projectPath: project.path, + builtProductsPath: project.path + ) } - public func checkTarget(_ guid: PIF.GUID, file: StaticString = #file, line: UInt = #line, body: ((PIFTargetTester) -> Void)? = nil) { + public func checkTarget( + _ guid: PIF.GUID, + file: StaticString = #file, + line: UInt = #line, + body: ((PIFTargetTester) -> Void)? = nil + ) { guard let baseTarget = baseTarget(withGUID: guid) else { let guids = project.targets.map { $0.guid }.joined(separator: ", ") return XCTFail("target \(guid) not found among \(guids)", file: file, line: line) @@ -71,13 +86,23 @@ public final class PIFProjectTester { body?(PIFTargetTester(target: target, targetMap: targetMap, fileMap: fileMap)) } - public func checkNoTarget(_ guid: PIF.GUID, file: StaticString = #file, line: UInt = #line, body: ((PIFTargetTester) -> Void)? = nil) { + public func checkNoTarget( + _ guid: PIF.GUID, + file: StaticString = #file, + line: UInt = #line, + body: ((PIFTargetTester) -> Void)? = nil + ) { if baseTarget(withGUID: guid) != nil { XCTFail("target \(guid) found", file: file, line: line) } } - public func checkAggregateTarget(_ guid: PIF.GUID, file: StaticString = #file, line: UInt = #line, body: ((PIFAggregateTargetTester) -> Void)? = nil) { + public func checkAggregateTarget( + _ guid: PIF.GUID, + file: StaticString = #file, + line: UInt = #line, + body: ((PIFAggregateTargetTester) -> Void)? = nil + ) { guard let baseTarget = baseTarget(withGUID: guid) else { let guids = project.targets.map { $0.guid }.joined(separator: ", ") return XCTFail("target \(guid) not found among \(guids)", file: file, line: line) @@ -90,7 +115,12 @@ public final class PIFProjectTester { body?(PIFAggregateTargetTester(target: target, targetMap: targetMap, fileMap: fileMap)) } - public func checkBuildConfiguration(_ name: String, file: StaticString = #file, line: UInt = #line, body: (PIFBuildConfigurationTester) -> Void) { + public func checkBuildConfiguration( + _ name: String, + file: StaticString = #file, + line: UInt = #line, + body: (PIFBuildConfigurationTester) -> Void + ) { guard let configuration = buildConfiguration(withName: name) else { let names = project.buildConfigurations.map { $0.name }.joined(separator: ", ") return XCTFail("build configuration \(name) not found among \(names)", file: file, line: line) @@ -151,7 +181,12 @@ public class PIFBaseTargetTester { }) } - public func checkBuildConfiguration(_ name: String, file: StaticString = #file, line: UInt = #line, body: (PIFBuildConfigurationTester) -> Void) { + public func checkBuildConfiguration( + _ name: String, + file: StaticString = #file, + line: UInt = #line, + body: (PIFBuildConfigurationTester) -> Void + ) { guard let configuration = buildConfiguration(withName: name) else { return XCTFail("build configuration \(name) not found", file: file, line: line) } @@ -163,19 +198,33 @@ public class PIFBaseTargetTester { return baseTarget.buildConfigurations.first { $0.name == name } } - public func checkImpartedBuildSettings(file: StaticString = #file, line: UInt = #line, _ body: (PIFBuildSettingsTester) -> Void) { - let buildSettingsTester = PIFBuildSettingsTester(buildSettings: baseTarget.buildConfigurations.first!.impartedBuildProperties.buildSettings) + public func checkImpartedBuildSettings( + file: StaticString = #file, + line: UInt = #line, + _ body: (PIFBuildSettingsTester) -> Void + ) { + let buildSettingsTester = PIFBuildSettingsTester( + buildSettings: baseTarget.buildConfigurations.first!.impartedBuildProperties.buildSettings + ) body(buildSettingsTester) } - public func checkAllImpartedBuildSettings(file: StaticString = #file, line: UInt = #line, _ body: (PIFBuildSettingsTester) -> Void) { - let buildSettingsTester = PIFBuildSettingsTester(buildSettings: baseTarget.buildConfigurations.first!.impartedBuildProperties.buildSettings) + public func checkAllImpartedBuildSettings( + file: StaticString = #file, + line: UInt = #line, + _ body: (PIFBuildSettingsTester) -> Void + ) { + let buildSettingsTester = PIFBuildSettingsTester( + buildSettings: baseTarget.buildConfigurations.first!.impartedBuildProperties.buildSettings + ) body(buildSettingsTester) buildSettingsTester.checkUncheckedSettings(file: file, line: line) } public func checkNoImpartedBuildSettings(file: StaticString = #file, line: UInt = #line) { - let buildSettingsTester = PIFBuildSettingsTester(buildSettings: baseTarget.buildConfigurations.first!.impartedBuildProperties.buildSettings) + let buildSettingsTester = PIFBuildSettingsTester( + buildSettings: baseTarget.buildConfigurations.first!.impartedBuildProperties.buildSettings + ) buildSettingsTester.checkUncheckedSettings(file: file, line: line) } } @@ -313,7 +362,12 @@ private func collectFiles( files[reference.guid] = referencePath.pathString } else if let group = reference as? PIF.Group { for child in group.children { - let childFiles = try collectFiles(from: child, parentPath: referencePath, projectPath: projectPath, builtProductsPath: builtProductsPath) + let childFiles = try collectFiles( + from: child, + parentPath: referencePath, + projectPath: projectPath, + builtProductsPath: builtProductsPath + ) files.merge(childFiles, uniquingKeysWith: { _, _ in fatalError("non-unique GUID") }) } } diff --git a/Sources/SPMTestSupport/SwiftPMProduct.swift b/Sources/SPMTestSupport/SwiftPMProduct.swift index b62a99b428c..1c10b1fda4e 100644 --- a/Sources/SPMTestSupport/SwiftPMProduct.swift +++ b/Sources/SPMTestSupport/SwiftPMProduct.swift @@ -128,7 +128,7 @@ extension SwiftPM { completeArgs += ["--package-path", packagePath.pathString] } completeArgs += args - + return try Process.popen(arguments: completeArgs, environment: environment) } } diff --git a/Sources/XCBuildSupport/PIFBuilder.swift b/Sources/XCBuildSupport/PIFBuilder.swift index 44117107238..3dda827da8e 100644 --- a/Sources/XCBuildSupport/PIFBuilder.swift +++ b/Sources/XCBuildSupport/PIFBuilder.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2014-2023 Apple Inc. and the Swift project authors +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information @@ -15,6 +15,8 @@ import Basics import PackageModel import PackageLoading import PackageGraph + +@_spi(SwiftPMInternal) import SPMBuildCore import func TSCBasic.topologicalSort @@ -22,6 +24,8 @@ import func TSCBasic.memoize /// The parameters required by `PIFBuilder`. struct PIFBuilderParameters { + /// Whether the toolchain supports `-package-name` option. + let isPackageAccessModifierSupported: Bool /// Whether or not build for testability is enabled. let enableTestability: Bool @@ -264,12 +268,12 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { super.init() - guid = package.pifProjectGUID - name = package.manifest.displayName // TODO: use identity instead? - path = package.path - projectDirectory = package.path - developmentRegion = package.manifest.defaultLocalization ?? "en" - binaryGroup = groupTree.addGroup(path: "/", sourceTree: .absolute, name: "Binaries") + self.guid = package.pifProjectGUID + self.name = package.manifest.displayName // TODO: use identity instead? + self.path = package.path + self.projectDirectory = package.path + self.developmentRegion = package.manifest.defaultLocalization ?? "en" + self.binaryGroup = groupTree.addGroup(path: "/", sourceTree: .absolute, name: "Binaries") // Configure the project-wide build settings. First we set those that are in common between the "Debug" and // "Release" configurations, and then we set those that are different. @@ -471,6 +475,8 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { settings[.CLANG_CXX_LANGUAGE_STANDARD] = clangTarget.cxxLanguageStandard } else if let swiftTarget = mainTarget.underlying as? SwiftTarget { settings[.SWIFT_VERSION] = swiftTarget.swiftVersion.description + + settings.addCommonSwiftSettings(package: self.package, target: mainTarget, parameters: self.parameters) } if let resourceBundle = addResourceBundle(for: mainTarget, in: pifTarget) { @@ -501,7 +507,7 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { let executableName: String let productType: PIF.Target.ProductType if product.type == .library(.dynamic) { - if parameters.shouldCreateDylibForDynamicProducts { + if self.parameters.shouldCreateDylibForDynamicProducts { pifTargetProductName = "lib\(product.name).dylib" executableName = pifTargetProductName productType = .dynamicLibrary @@ -520,7 +526,7 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { // depend on. XCBuild will not produce a separate artifact for a package product, but will instead consider any // dependency on the package product to be a dependency on the whole set of targets on which the package product // depends. - let pifTarget = addTarget( + let pifTarget = self.addTarget( guid: product.pifTargetGUID, name: targetName(for: product), productType: productType, @@ -651,6 +657,8 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { settings[.SWIFT_OBJC_INTERFACE_HEADER_DIR] = "$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)" settings[.SWIFT_OBJC_INTERFACE_HEADER_NAME] = "\(target.name)-Swift.h" + settings.addCommonSwiftSettings(package: self.package, target: target, parameters: self.parameters) + moduleMapFileContents = """ module \(target.c99name) { header "\(target.name)-Swift.h" @@ -1663,6 +1671,22 @@ extension PIF.PlatformFilter { ] } +private extension PIF.BuildSettings { + mutating func addCommonSwiftSettings( + package: ResolvedPackage, + target: ResolvedTarget, + parameters: PIFBuilderParameters + ) { + let packageOptions = package.packageNameArgument( + target: target, + isPackageNameSupported: parameters.isPackageAccessModifierSupported + ) + if !packageOptions.isEmpty { + self[.OTHER_SWIFT_FLAGS] = packageOptions + } + } +} + private extension PIF.BuildSettings.Platform { static func from(platform: PackageModel.Platform) -> PIF.BuildSettings.Platform? { switch platform { diff --git a/Sources/XCBuildSupport/XcodeBuildSystem.swift b/Sources/XCBuildSupport/XcodeBuildSystem.swift index 8399a96e0c8..263027977f5 100644 --- a/Sources/XCBuildSupport/XcodeBuildSystem.swift +++ b/Sources/XCBuildSupport/XcodeBuildSystem.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Copyright (c) 2020-2024 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information @@ -16,6 +16,8 @@ import Dispatch import class Foundation.JSONEncoder import PackageGraph import PackageModel + +@_spi(SwiftPMInternal) import SPMBuildCore import protocol TSCBasic.OutputByteStream @@ -320,6 +322,7 @@ extension BuildConfiguration { extension PIFBuilderParameters { public init(_ buildParameters: BuildParameters) { self.init( + isPackageAccessModifierSupported: buildParameters.driverParameters.isPackageAccessModifierSupported, enableTestability: buildParameters.testingParameters.enableTestability, shouldCreateDylibForDynamicProducts: buildParameters.shouldCreateDylibForDynamicProducts, toolchainLibDir: (try? buildParameters.toolchain.toolchainLibDir) ?? .root, diff --git a/Sources/swift-bootstrap/CMakeLists.txt b/Sources/swift-bootstrap/CMakeLists.txt index 6371d1a01d4..e8cb9c4e879 100644 --- a/Sources/swift-bootstrap/CMakeLists.txt +++ b/Sources/swift-bootstrap/CMakeLists.txt @@ -15,6 +15,7 @@ target_link_libraries(swift-bootstrap PRIVATE PackageGraph PackageLoading PackageModel + SwiftDriver TSCBasic TSCUtility) diff --git a/Sources/swift-bootstrap/main.swift b/Sources/swift-bootstrap/main.swift index 6895b9c6b42..55f6b58e000 100644 --- a/Sources/swift-bootstrap/main.swift +++ b/Sources/swift-bootstrap/main.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2022-2024 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information @@ -14,6 +14,10 @@ import ArgumentParser import Basics import Build import Dispatch + +@_spi(SwiftPMInternal) +import DriverSupport + import Foundation import OrderedCollections import PackageGraph @@ -290,7 +294,11 @@ struct SwiftBootstrapBuildTool: ParsableCommand { isXcodeBuildSystemEnabled: buildSystem == .xcode, driverParameters: .init( explicitTargetDependencyImportCheckingMode: explicitTargetDependencyImportCheck == .error ? .error : .none, - useIntegratedSwiftDriver: useIntegratedSwiftDriver + useIntegratedSwiftDriver: useIntegratedSwiftDriver, + isPackageAccessModifierSupported: DriverSupport.isPackageNameSupported( + toolchain: targetToolchain, + fileSystem: self.fileSystem + ) ), linkingParameters: .init( shouldDisableLocalRpath: shouldDisableLocalRpath @@ -304,7 +312,6 @@ struct SwiftBootstrapBuildTool: ParsableCommand { let packageGraphLoader = { try self.loadPackageGraph(packagePath: packagePath, manifestLoader: manifestLoader) - } switch buildSystem { diff --git a/Tests/BuildTests/BuildPlanTests.swift b/Tests/BuildTests/BuildPlanTests.swift index 0ab7adc3bd6..e461ac1e93d 100644 --- a/Tests/BuildTests/BuildPlanTests.swift +++ b/Tests/BuildTests/BuildPlanTests.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2014-2021 Apple Inc. and the Swift project authors +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information @@ -12,7 +12,11 @@ @testable import Basics @testable import Build + +@testable +@_spi(SwiftPMInternal) import DriverSupport + @testable import PackageGraph import PackageLoading @testable import PackageModel @@ -645,6 +649,37 @@ final class BuildPlanTests: XCTestCase { } } + #if os(macOS) + func testPackageNameFlagXCBuild() throws { + let isFlagSupportedInDriver = try DriverSupport.checkToolchainDriverFlags( + flags: ["package-name"], + toolchain: UserToolchain.default, + fileSystem: localFileSystem + ) + try fixture(name: "Miscellaneous/PackageNameFlag") { fixturePath in + let (stdout, _) = try executeSwiftBuild( + fixturePath.appending("appPkg"), + extraArgs: ["--build-system", "xcode", "-vv"] + ) + XCTAssertMatch(stdout, .contains("-module-name Foo")) + XCTAssertMatch(stdout, .contains("-module-name Zoo")) + XCTAssertMatch(stdout, .contains("-module-name Bar")) + XCTAssertMatch(stdout, .contains("-module-name Baz")) + XCTAssertMatch(stdout, .contains("-module-name App")) + XCTAssertMatch(stdout, .contains("-module-name exe")) + if isFlagSupportedInDriver { + XCTAssertMatch(stdout, .contains("-package-name apppkg")) + XCTAssertMatch(stdout, .contains("-package-name foopkg")) + // the flag is not supported if tools-version < 5.9 + XCTAssertNoMatch(stdout, .contains("-package-name barpkg")) + } else { + XCTAssertNoMatch(stdout, .contains("-package-name")) + } + XCTAssertMatch(stdout, .contains("Build succeeded")) + } + } + #endif + func testTargetsWithPackageAccess() throws { let isFlagSupportedInDriver = try DriverSupport.checkToolchainDriverFlags( flags: ["package-name"], diff --git a/Tests/CommandsTests/APIDiffTests.swift b/Tests/CommandsTests/APIDiffTests.swift index 7ab9a5fdd20..0f700957ac8 100644 --- a/Tests/CommandsTests/APIDiffTests.swift +++ b/Tests/CommandsTests/APIDiffTests.swift @@ -13,7 +13,10 @@ import Basics import Build import Commands + +@_spi(SwiftPMInternal) import DriverSupport + import Foundation import PackageModel import SourceControl diff --git a/Tests/XCBuildSupportTests/PIFBuilderTests.swift b/Tests/XCBuildSupportTests/PIFBuilderTests.swift index 79fc28be3b5..f1171d8c706 100644 --- a/Tests/XCBuildSupportTests/PIFBuilderTests.swift +++ b/Tests/XCBuildSupportTests/PIFBuilderTests.swift @@ -2218,7 +2218,7 @@ class PIFBuilderTests: XCTestCase { } } - func testBuildSettings() throws { + func buildSettingsTestCase(isPackageAccessModifierSupported: Bool) throws { #if !os(macOS) try XCTSkipIf(true, "test is only supported on macOS") #endif @@ -2228,6 +2228,8 @@ class PIFBuilderTests: XCTestCase { "/Foo/Sources/FooTests/FooTests.swift" ) + let toolsVersion: ToolsVersion = if isPackageAccessModifierSupported { .v5_9 } else { .v5 } + let mainTargetType: TargetDescription.TargetType = if toolsVersion >= .v5_9 { .executable } else { .regular } let observability = ObservabilitySystem.makeForTesting() let graph = try loadModulesGraph( fileSystem: fs, @@ -2235,28 +2237,32 @@ class PIFBuilderTests: XCTestCase { Manifest.createRootManifest( displayName: "Foo", path: "/Foo", - toolsVersion: .v5, + toolsVersion: toolsVersion, products: [ .init(name: "FooLib", type: .library(.automatic), targets: ["FooLib"]), ], targets: [ - .init(name: "foo", settings: [ - .init( - tool: .c, - kind: .define("ENABLE_BEST_MODE")), - .init( - tool: .cxx, - kind: .headerSearchPath("some/path"), - condition: .init(platformNames: ["macos"])), - .init( - tool: .linker, - kind: .linkedLibrary("z"), - condition: .init(config: "debug")), - .init( - tool: .swift, - kind: .unsafeFlags(["-secret", "value"]), - condition: .init(platformNames: ["macos", "linux"], config: "release")), - ]), + .init( + name: "foo", + type: mainTargetType, + settings: [ + .init( + tool: .c, + kind: .define("ENABLE_BEST_MODE")), + .init( + tool: .cxx, + kind: .headerSearchPath("some/path"), + condition: .init(platformNames: ["macos"])), + .init( + tool: .linker, + kind: .linkedLibrary("z"), + condition: .init(config: "debug")), + .init( + tool: .swift, + kind: .unsafeFlags(["-secret", "value"]), + condition: .init(platformNames: ["macos", "linux"], config: "release")), + ] + ), .init(name: "FooLib", settings: [ .init( tool: .c, @@ -2291,7 +2297,8 @@ class PIFBuilderTests: XCTestCase { kind: .unsafeFlags(["-secret", "value"]), condition: .init(platformNames: ["macos", "linux"], config: "release")), ]), - ]), + ] + ), ], shouldCreateMultipleTestProducts: true, observabilityScope: observability.topScope @@ -2299,7 +2306,7 @@ class PIFBuilderTests: XCTestCase { let builder = PIFBuilder( graph: graph, - parameters: .mock(), + parameters: .mock(isPackageAccessModifierSupported: isPackageAccessModifierSupported), fileSystem: fs, observabilityScope: observability.topScope ) @@ -2307,6 +2314,12 @@ class PIFBuilderTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) + let packageNameOptions = if isPackageAccessModifierSupported { + ["-package-name", "foo"] + } else { + [String]?.none + } + try PIFTester(pif) { workspace in try workspace.checkProject("PACKAGE:/Foo") { project in project.checkTarget("PACKAGE-PRODUCT:foo") { target in @@ -2319,7 +2332,7 @@ class PIFBuilderTests: XCTestCase { "/Foo/Sources/foo/some/path" ]) XCTAssertEqual(settings[.OTHER_LDFLAGS], ["$(inherited)", "-lz"]) - XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS], nil) + XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS], packageNameOptions) } } @@ -2332,7 +2345,7 @@ class PIFBuilderTests: XCTestCase { "/Foo/Sources/foo/some/path" ]) XCTAssertEqual(settings[.OTHER_LDFLAGS], nil) - XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS], nil) + XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS], packageNameOptions) XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS, for: .macOS], ["$(inherited)", "-secret", "value"]) XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS, for: .linux], ["$(inherited)", "-secret", "value"]) } @@ -2371,7 +2384,7 @@ class PIFBuilderTests: XCTestCase { "/Foo/Sources/FooLib/some/path" ]) XCTAssertEqual(settings[.OTHER_LDFLAGS], ["$(inherited)", "-lz"]) - XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS], nil) + XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS], packageNameOptions) } } @@ -2384,7 +2397,7 @@ class PIFBuilderTests: XCTestCase { "/Foo/Sources/FooLib/some/path" ]) XCTAssertEqual(settings[.OTHER_LDFLAGS], nil) - XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS], nil) + XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS], packageNameOptions) XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS, for: .macOS], ["$(inherited)", "-secret", "value"]) XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS, for: .linux], ["$(inherited)", "-secret", "value"]) } @@ -2408,7 +2421,7 @@ class PIFBuilderTests: XCTestCase { "/Foo/Sources/FooTests/some/path" ]) XCTAssertEqual(settings[.OTHER_LDFLAGS], ["$(inherited)", "-lz"]) - XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS], nil) + XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS], packageNameOptions) } } @@ -2421,7 +2434,7 @@ class PIFBuilderTests: XCTestCase { "/Foo/Sources/FooTests/some/path" ]) XCTAssertEqual(settings[.OTHER_LDFLAGS], nil) - XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS], nil) + XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS], packageNameOptions) XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS, for: .macOS], ["$(inherited)", "-secret", "value"]) XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS, for: .linux], ["$(inherited)", "-secret", "value"]) } @@ -2431,6 +2444,14 @@ class PIFBuilderTests: XCTestCase { } } + func testBuildSettings() throws { + try buildSettingsTestCase(isPackageAccessModifierSupported: false) + } + + func testBuildSettingsPackageAccess() throws { + try buildSettingsTestCase(isPackageAccessModifierSupported: true) + } + func testConditionalDependencies() throws { #if !os(macOS) try XCTSkipIf(true, "test is only supported on macOS") @@ -2620,9 +2641,11 @@ class PIFBuilderTests: XCTestCase { extension PIFBuilderParameters { static func mock( + isPackageAccessModifierSupported: Bool = false, shouldCreateDylibForDynamicProducts: Bool = false ) -> Self { PIFBuilderParameters( + isPackageAccessModifierSupported: isPackageAccessModifierSupported, enableTestability: false, shouldCreateDylibForDynamicProducts: shouldCreateDylibForDynamicProducts, toolchainLibDir: "/toolchain/lib", From f533e1b3d9f52378aceb4ed1d35dc20968238463 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Sat, 24 Feb 2024 14:42:07 -1000 Subject: [PATCH 025/159] Generalize test to handle the compiler's new diagnostic printing style (#7368) --- Tests/FunctionalTests/MiscellaneousTests.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Tests/FunctionalTests/MiscellaneousTests.swift b/Tests/FunctionalTests/MiscellaneousTests.swift index fb3f5033ed1..b5e5dc3e28f 100644 --- a/Tests/FunctionalTests/MiscellaneousTests.swift +++ b/Tests/FunctionalTests/MiscellaneousTests.swift @@ -89,7 +89,8 @@ class MiscellaneousTestCase: XCTestCase { return XCTFail("failed in an unexpected manner: \(error)") } XCTAssertMatch(error.stdout + error.stderr, .contains("Compiling CompileFails Foo.swift")) - XCTAssertMatch(error.stdout + error.stderr, .regex("error: .*\n.*compile_failure")) + XCTAssertMatch(error.stdout + error.stderr, .regex(".*compile_failure.*")) + XCTAssertMatch(error.stdout + error.stderr, .regex(".*error:.*")) } } } From d5787ea534ead1cb9a770db03da7cc065df4751a Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Sun, 25 Feb 2024 11:07:50 +0000 Subject: [PATCH 026/159] Rename `Swift*Tool` to `Swift*Command` (#7336) This removes an existing point of confusion, where `SwiftBuildTool` that handles `swift build` CLI invocations could be misunderstood as something responsible for build tools in plugins. It becomes especially problematic when working on the plugins codebase. Additionally, `SwiftTool` is a grab bag reference type for storing state injected into a CLI command, so we should rename it to `SwiftCommandState` for clarity. --- .../xcschemes/SwiftPM-Package.xcscheme | 911 ------------------ Package.swift | 22 +- Sources/CMakeLists.txt | 8 +- Sources/Commands/CMakeLists.txt | 46 +- ...e.swift => CommandWorkspaceDelegate.swift} | 4 +- .../APIDiff.swift | 28 +- .../ArchiveSource.swift | 12 +- .../CompletionCommand.swift} | 22 +- .../ComputeChecksum.swift | 8 +- .../Config.swift | 46 +- .../Describe.swift | 10 +- .../DumpCommands.swift | 36 +- .../EditCommands.swift | 22 +- .../Format.swift | 12 +- .../Init.swift | 10 +- .../InstalledPackages.swift | 6 +- .../Learn.swift | 10 +- .../PluginCommand.swift | 50 +- .../ResetCommands.swift | 14 +- .../Resolve.swift | 20 +- .../ShowDependencies.swift | 6 +- .../SwiftPackageCommand.swift} | 12 +- .../ToolsVersionCommand.swift | 12 +- .../Update.swift | 14 +- Sources/Commands/Snippets/CardStack.swift | 10 +- .../Commands/Snippets/Cards/SnippetCard.swift | 6 +- .../Snippets/Cards/SnippetGroupCard.swift | 6 +- Sources/Commands/Snippets/Cards/TopCard.swift | 10 +- ...uildTool.swift => SwiftBuildCommand.swift} | 22 +- ...iftRunTool.swift => SwiftRunCommand.swift} | 60 +- ...tTestTool.swift => SwiftTestCommand.swift} | 269 ++++-- Sources/Commands/Utilities/APIDigester.swift | 14 +- .../Commands/Utilities/PluginDelegate.swift | 52 +- .../Commands/Utilities/TestingSupport.swift | 26 +- Sources/CoreCommands/BuildSystemSupport.swift | 50 +- Sources/CoreCommands/CMakeLists.txt | 4 +- ...=> SwiftCommandObservabilityHandler.swift} | 10 +- ...wiftTool.swift => SwiftCommandState.swift} | 42 +- .../PackageCollectionsCommand.swift} | 41 +- .../PackageRegistryCommand+Auth.swift} | 32 +- .../PackageRegistryCommand+Publish.swift} | 26 +- .../PackageRegistryCommand.swift} | 26 +- .../CMakeLists.txt | 10 +- .../ConfigurationSubcommand.swift | 0 .../Configuration/ConfigureSwiftSDK.swift | 0 .../Configuration/ResetConfiguration.swift | 0 .../Configuration/SetConfiguration.swift | 0 .../Configuration/ShowConfiguration.swift | 0 .../InstallSwiftSDK.swift | 0 .../ListSwiftSDKs.swift | 0 .../README.md | 0 .../RemoveSwiftSDK.swift | 0 .../SwiftSDKCommand.swift} | 2 +- .../SwiftSDKSubcommand.swift | 2 +- Sources/swift-build/main.swift | 2 +- Sources/swift-experimental-sdk/CMakeLists.txt | 2 +- .../swift-experimental-sdk/Entrypoint.swift | 4 +- .../swift-package-collection/Entrypoint.swift | 5 +- Sources/swift-package-manager/SwiftPM.swift | 20 +- Sources/swift-package-registry/runner.swift | 4 +- Sources/swift-package/Entrypoint.swift | 2 +- Sources/swift-run/main.swift | 2 +- Sources/swift-test/main.swift | 2 +- ...oolTests.swift => BuildCommandTests.swift} | 6 +- ...lTests.swift => PackageCommandTests.swift} | 6 +- ...wift => PackageRegistryCommandTests.swift} | 18 +- ...nToolTests.swift => RunCommandTests.swift} | 3 +- ...sts.swift => SwiftCommandStateTests.swift} | 42 +- ...ToolTests.swift => TestCommandTests.swift} | 2 +- 69 files changed, 667 insertions(+), 1514 deletions(-) delete mode 100644 .swiftpm/xcode/xcshareddata/xcschemes/SwiftPM-Package.xcscheme rename Sources/Commands/{ToolWorkspaceDelegate.swift => CommandWorkspaceDelegate.swift} (99%) rename Sources/Commands/{PackageTools => PackageCommands}/APIDiff.swift (90%) rename Sources/Commands/{PackageTools => PackageCommands}/ArchiveSource.swift (89%) rename Sources/Commands/{PackageTools/CompletionTool.swift => PackageCommands/CompletionCommand.swift} (84%) rename Sources/Commands/{PackageTools => PackageCommands}/ComputeChecksum.swift (83%) rename Sources/Commands/{PackageTools => PackageCommands}/Config.swift (79%) rename Sources/Commands/{PackageTools => PackageCommands}/Describe.swift (87%) rename Sources/Commands/{PackageTools => PackageCommands}/DumpCommands.swift (78%) rename Sources/Commands/{PackageTools => PackageCommands}/EditCommands.swift (75%) rename Sources/Commands/{PackageTools => PackageCommands}/Format.swift (85%) rename Sources/Commands/{PackageTools => PackageCommands}/Init.swift (90%) rename Sources/Commands/{PackageTools => PackageCommands}/InstalledPackages.swift (97%) rename Sources/Commands/{PackageTools => PackageCommands}/Learn.swift (93%) rename Sources/Commands/{PackageTools => PackageCommands}/PluginCommand.swift (91%) rename Sources/Commands/{PackageTools => PackageCommands}/ResetCommands.swift (68%) rename Sources/Commands/{PackageTools => PackageCommands}/Resolve.swift (76%) rename Sources/Commands/{PackageTools => PackageCommands}/ShowDependencies.swift (95%) rename Sources/Commands/{PackageTools/SwiftPackageTool.swift => PackageCommands/SwiftPackageCommand.swift} (93%) rename Sources/Commands/{PackageTools => PackageCommands}/ToolsVersionCommand.swift (87%) rename Sources/Commands/{PackageTools => PackageCommands}/Update.swift (86%) rename Sources/Commands/{SwiftBuildTool.swift => SwiftBuildCommand.swift} (86%) rename Sources/Commands/{SwiftRunTool.swift => SwiftRunCommand.swift} (81%) rename Sources/Commands/{SwiftTestTool.swift => SwiftTestCommand.swift} (82%) rename Sources/CoreCommands/{SwiftToolObservabilityHandler.swift => SwiftCommandObservabilityHandler.swift} (95%) rename Sources/CoreCommands/{SwiftTool.swift => SwiftCommandState.swift} (96%) rename Sources/{PackageCollectionsTool/SwiftPackageCollectionsTool.swift => PackageCollectionsCommand/PackageCollectionsCommand.swift} (91%) rename Sources/{PackageRegistryTool/PackageRegistryTool+Auth.swift => PackageRegistryCommand/PackageRegistryCommand+Auth.swift} (91%) rename Sources/{PackageRegistryTool/PackageRegistryTool+Publish.swift => PackageRegistryCommand/PackageRegistryCommand+Publish.swift} (95%) rename Sources/{PackageRegistryTool/PackageRegistryTool.swift => PackageRegistryCommand/PackageRegistryCommand.swift} (86%) rename Sources/{SwiftSDKTool => SwiftSDKCommand}/CMakeLists.txt (83%) rename Sources/{SwiftSDKTool => SwiftSDKCommand}/Configuration/ConfigurationSubcommand.swift (100%) rename Sources/{SwiftSDKTool => SwiftSDKCommand}/Configuration/ConfigureSwiftSDK.swift (100%) rename Sources/{SwiftSDKTool => SwiftSDKCommand}/Configuration/ResetConfiguration.swift (100%) rename Sources/{SwiftSDKTool => SwiftSDKCommand}/Configuration/SetConfiguration.swift (100%) rename Sources/{SwiftSDKTool => SwiftSDKCommand}/Configuration/ShowConfiguration.swift (100%) rename Sources/{SwiftSDKTool => SwiftSDKCommand}/InstallSwiftSDK.swift (100%) rename Sources/{SwiftSDKTool => SwiftSDKCommand}/ListSwiftSDKs.swift (100%) rename Sources/{SwiftSDKTool => SwiftSDKCommand}/README.md (100%) rename Sources/{SwiftSDKTool => SwiftSDKCommand}/RemoveSwiftSDK.swift (100%) rename Sources/{SwiftSDKTool/SwiftSDKTool.swift => SwiftSDKCommand/SwiftSDKCommand.swift} (95%) rename Sources/{SwiftSDKTool => SwiftSDKCommand}/SwiftSDKSubcommand.swift (96%) rename Tests/CommandsTests/{BuildToolTests.swift => BuildCommandTests.swift} (99%) rename Tests/CommandsTests/{PackageToolTests.swift => PackageCommandTests.swift} (99%) rename Tests/CommandsTests/{PackageRegistryToolTests.swift => PackageRegistryCommandTests.swift} (97%) rename Tests/CommandsTests/{RunToolTests.swift => RunCommandTests.swift} (99%) rename Tests/CommandsTests/{SwiftToolTests.swift => SwiftCommandStateTests.swift} (90%) rename Tests/CommandsTests/{TestToolTests.swift => TestCommandTests.swift} (99%) diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/SwiftPM-Package.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/SwiftPM-Package.xcscheme deleted file mode 100644 index 447e5415e3b..00000000000 --- a/.swiftpm/xcode/xcshareddata/xcschemes/SwiftPM-Package.xcscheme +++ /dev/null @@ -1,911 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Package.swift b/Package.swift index 1b67941a013..14110d94038 100644 --- a/Package.swift +++ b/Package.swift @@ -416,7 +416,7 @@ let package = Package( .target( /** Interacts with Swift SDKs used for cross-compilation */ - name: "SwiftSDKTool", + name: "SwiftSDKCommand", dependencies: [ .product(name: "ArgumentParser", package: "swift-argument-parser"), "Basics", @@ -429,7 +429,7 @@ let package = Package( .target( /** Interacts with package collections */ - name: "PackageCollectionsTool", + name: "PackageCollectionsCommand", dependencies: [ .product(name: "ArgumentParser", package: "swift-argument-parser"), "Basics", @@ -442,7 +442,7 @@ let package = Package( .target( /** Interact with package registry */ - name: "PackageRegistryTool", + name: "PackageRegistryCommand", dependencies: [ .product(name: "ArgumentParser", package: "swift-argument-parser"), "Basics", @@ -488,7 +488,7 @@ let package = Package( .executableTarget( /** Interacts with Swift SDKs used for cross-compilation */ name: "swift-experimental-sdk", - dependencies: ["Commands", "SwiftSDKTool"], + dependencies: ["Commands", "SwiftSDKCommand"], exclude: ["CMakeLists.txt"] ), .executableTarget( @@ -506,24 +506,24 @@ let package = Package( .executableTarget( /** Interacts with package collections */ name: "swift-package-collection", - dependencies: ["Commands", "PackageCollectionsTool"] + dependencies: ["Commands", "PackageCollectionsCommand"] ), .executableTarget( - /** Multi-tool entry point for SwiftPM. */ + /** Multi-command entry point for SwiftPM. */ name: "swift-package-manager", dependencies: [ "Basics", "Commands", - "SwiftSDKTool", - "PackageCollectionsTool", - "PackageRegistryTool" + "SwiftSDKCommand", + "PackageCollectionsCommand", + "PackageRegistryCommand" ], linkerSettings: swiftpmLinkSettings ), .executableTarget( /** Interact with package registry */ name: "swift-package-registry", - dependencies: ["Commands", "PackageRegistryTool"] + dependencies: ["Commands", "PackageRegistryCommand"] ), // MARK: Support for Swift macros, should eventually move to a plugin-based solution @@ -721,7 +721,7 @@ if ProcessInfo.processInfo.environment["SWIFTCI_DISABLE_SDK_DEPENDENT_TESTS"] == "Build", "Commands", "PackageModel", - "PackageRegistryTool", + "PackageRegistryCommand", "SourceControl", "SPMTestSupport", "Workspace", diff --git a/Sources/CMakeLists.txt b/Sources/CMakeLists.txt index a148ae10fb8..82562eb7a01 100644 --- a/Sources/CMakeLists.txt +++ b/Sources/CMakeLists.txt @@ -9,13 +9,11 @@ add_compile_definitions(SKIP_RESOURCE_SUPPORT) add_compile_definitions(USE_IMPL_ONLY_IMPORTS) -add_subdirectory(SPMSQLite3) add_subdirectory(Basics) add_subdirectory(Build) add_subdirectory(Commands) add_subdirectory(CompilerPluginSupport) add_subdirectory(CoreCommands) -add_subdirectory(SwiftSDKTool) add_subdirectory(DriverSupport) add_subdirectory(LLBuildManifest) add_subdirectory(PackageDescription) @@ -26,14 +24,16 @@ add_subdirectory(PackageModel) add_subdirectory(PackagePlugin) add_subdirectory(PackageRegistry) add_subdirectory(PackageSigning) -add_subdirectory(SPMBuildCore) -add_subdirectory(SPMLLBuild) add_subdirectory(SourceControl) add_subdirectory(SourceKitLSPAPI) +add_subdirectory(SPMBuildCore) +add_subdirectory(SPMLLBuild) +add_subdirectory(SPMSQLite3) add_subdirectory(swift-bootstrap) add_subdirectory(swift-build) add_subdirectory(swift-experimental-sdk) add_subdirectory(swift-package) add_subdirectory(swift-run) add_subdirectory(swift-test) +add_subdirectory(SwiftSDKCommand) add_subdirectory(Workspace) diff --git a/Sources/Commands/CMakeLists.txt b/Sources/Commands/CMakeLists.txt index eb8242ee3e0..3f2275fbe02 100644 --- a/Sources/Commands/CMakeLists.txt +++ b/Sources/Commands/CMakeLists.txt @@ -7,25 +7,25 @@ # See http://swift.org/CONTRIBUTORS.txt for Swift project authors add_library(Commands - PackageTools/APIDiff.swift - PackageTools/ArchiveSource.swift - PackageTools/CompletionTool.swift - PackageTools/ComputeChecksum.swift - PackageTools/Config.swift - PackageTools/Describe.swift - PackageTools/DumpCommands.swift - PackageTools/EditCommands.swift - PackageTools/Format.swift - PackageTools/Init.swift - PackageTools/InstalledPackages.swift - PackageTools/Learn.swift - PackageTools/PluginCommand.swift - PackageTools/ResetCommands.swift - PackageTools/Resolve.swift - PackageTools/ShowDependencies.swift - PackageTools/SwiftPackageTool.swift - PackageTools/ToolsVersionCommand.swift - PackageTools/Update.swift + PackageCommands/APIDiff.swift + PackageCommands/ArchiveSource.swift + PackageCommands/CompletionCommand.swift + PackageCommands/ComputeChecksum.swift + PackageCommands/Config.swift + PackageCommands/Describe.swift + PackageCommands/DumpCommands.swift + PackageCommands/EditCommands.swift + PackageCommands/Format.swift + PackageCommands/Init.swift + PackageCommands/InstalledPackages.swift + PackageCommands/Learn.swift + PackageCommands/PluginCommand.swift + PackageCommands/ResetCommands.swift + PackageCommands/Resolve.swift + PackageCommands/ShowDependencies.swift + PackageCommands/SwiftPackageCommand.swift + PackageCommands/ToolsVersionCommand.swift + PackageCommands/Update.swift Snippets/CardEvent.swift Snippets/Cards/SnippetCard.swift Snippets/Cards/SnippetGroupCard.swift @@ -33,10 +33,10 @@ add_library(Commands Snippets/CardStack.swift Snippets/Card.swift Snippets/Colorful.swift - SwiftBuildTool.swift - SwiftRunTool.swift - SwiftTestTool.swift - ToolWorkspaceDelegate.swift + SwiftBuildCommand.swift + SwiftRunCommand.swift + SwiftTestCommand.swift + CommandWorkspaceDelegate.swift Utilities/APIDigester.swift Utilities/DependenciesSerializer.swift Utilities/DescribedPackage.swift diff --git a/Sources/Commands/ToolWorkspaceDelegate.swift b/Sources/Commands/CommandWorkspaceDelegate.swift similarity index 99% rename from Sources/Commands/ToolWorkspaceDelegate.swift rename to Sources/Commands/CommandWorkspaceDelegate.swift index 983b0a75a68..accec9fe9a3 100644 --- a/Sources/Commands/ToolWorkspaceDelegate.swift +++ b/Sources/Commands/CommandWorkspaceDelegate.swift @@ -24,7 +24,7 @@ import Workspace import protocol TSCBasic.OutputByteStream import struct TSCUtility.Version -class ToolWorkspaceDelegate: WorkspaceDelegate { +final class CommandWorkspaceDelegate: WorkspaceDelegate { private struct DownloadProgress { let bytesDownloaded: Int64 let totalBytesToDownload: Int64 @@ -267,7 +267,7 @@ class ToolWorkspaceDelegate: WorkspaceDelegate { public extension _SwiftCommand { var workspaceDelegateProvider: WorkspaceDelegateProvider { return { - ToolWorkspaceDelegate( + CommandWorkspaceDelegate( observabilityScope: $0, outputHandler: $1, progressHandler: $2, diff --git a/Sources/Commands/PackageTools/APIDiff.swift b/Sources/Commands/PackageCommands/APIDiff.swift similarity index 90% rename from Sources/Commands/PackageTools/APIDiff.swift rename to Sources/Commands/PackageCommands/APIDiff.swift index bdcc0826525..acb38e87ed0 100644 --- a/Sources/Commands/PackageTools/APIDiff.swift +++ b/Sources/Commands/PackageCommands/APIDiff.swift @@ -74,21 +74,21 @@ struct APIDiff: SwiftCommand { @Flag(help: "Regenerate the API baseline, even if an existing one is available.") var regenerateBaseline: Bool = false - func run(_ swiftTool: SwiftTool) throws { - let apiDigesterPath = try swiftTool.getTargetToolchain().getSwiftAPIDigester() - let apiDigesterTool = SwiftAPIDigester(fileSystem: swiftTool.fileSystem, tool: apiDigesterPath) + func run(_ swiftCommandState: SwiftCommandState) throws { + let apiDigesterPath = try swiftCommandState.getTargetToolchain().getSwiftAPIDigester() + let apiDigesterTool = SwiftAPIDigester(fileSystem: swiftCommandState.fileSystem, tool: apiDigesterPath) - let packageRoot = try globalOptions.locations.packageDirectory ?? swiftTool.getPackageRoot() + let packageRoot = try globalOptions.locations.packageDirectory ?? swiftCommandState.getPackageRoot() let repository = GitRepository(path: packageRoot) let baselineRevision = try repository.resolveRevision(identifier: treeish) // We turn build manifest caching off because we need the build plan. - let buildSystem = try swiftTool.createBuildSystem(explicitBuildSystem: .native, cacheBuildManifest: false) + let buildSystem = try swiftCommandState.createBuildSystem(explicitBuildSystem: .native, cacheBuildManifest: false) let packageGraph = try buildSystem.getPackageGraph() let modulesToDiff = try determineModulesToDiff( packageGraph: packageGraph, - observabilityScope: swiftTool.observabilityScope + observabilityScope: swiftCommandState.observabilityScope ) // Build the current package. @@ -97,19 +97,19 @@ struct APIDiff: SwiftCommand { // Dump JSON for the baseline package. let baselineDumper = try APIDigesterBaselineDumper( baselineRevision: baselineRevision, - packageRoot: swiftTool.getPackageRoot(), + packageRoot: swiftCommandState.getPackageRoot(), productsBuildParameters: try buildSystem.buildPlan.destinationBuildParameters, toolsBuildParameters: try buildSystem.buildPlan.toolsBuildParameters, apiDigesterTool: apiDigesterTool, - observabilityScope: swiftTool.observabilityScope + observabilityScope: swiftCommandState.observabilityScope ) let baselineDir = try baselineDumper.emitAPIBaseline( for: modulesToDiff, at: overrideBaselineDir, force: regenerateBaseline, - logLevel: swiftTool.logLevel, - swiftTool: swiftTool + logLevel: swiftCommandState.logLevel, + swiftCommandState: swiftCommandState ) let results = ThreadSafeArrayStore() @@ -119,7 +119,7 @@ struct APIDiff: SwiftCommand { for module in modulesToDiff { let moduleBaselinePath = baselineDir.appending("\(module).json") - guard swiftTool.fileSystem.exists(moduleBaselinePath) else { + guard swiftCommandState.fileSystem.exists(moduleBaselinePath) else { print("\nSkipping \(module) because it does not exist in the baseline") skippedModules.insert(module) continue @@ -136,7 +136,7 @@ struct APIDiff: SwiftCommand { results.append(comparisonResult) } } catch { - swiftTool.observabilityScope.emit(error: "failed to compare API to baseline", underlyingError: error) + swiftCommandState.observabilityScope.emit(error: "failed to compare API to baseline", underlyingError: error) } semaphore.signal() } @@ -148,11 +148,11 @@ struct APIDiff: SwiftCommand { .subtracting(skippedModules) .subtracting(results.map(\.moduleName)) for failedModule in failedModules { - swiftTool.observabilityScope.emit(error: "failed to read API digester output for \(failedModule)") + swiftCommandState.observabilityScope.emit(error: "failed to read API digester output for \(failedModule)") } for result in results.get() { - try self.printComparisonResult(result, observabilityScope: swiftTool.observabilityScope) + try self.printComparisonResult(result, observabilityScope: swiftCommandState.observabilityScope) } guard failedModules.isEmpty && results.get().allSatisfy(\.hasNoAPIBreakingChanges) else { diff --git a/Sources/Commands/PackageTools/ArchiveSource.swift b/Sources/Commands/PackageCommands/ArchiveSource.swift similarity index 89% rename from Sources/Commands/PackageTools/ArchiveSource.swift rename to Sources/Commands/PackageCommands/ArchiveSource.swift index c9c997f1395..01d2b7695ef 100644 --- a/Sources/Commands/PackageTools/ArchiveSource.swift +++ b/Sources/Commands/PackageCommands/ArchiveSource.swift @@ -15,7 +15,7 @@ import Basics import CoreCommands import SourceControl -extension SwiftPackageTool { +extension SwiftPackageCommand { struct ArchiveSource: SwiftCommand { static let configuration = CommandConfiguration( commandName: "archive-source", @@ -31,23 +31,23 @@ extension SwiftPackageTool { ) var output: AbsolutePath? - func run(_ swiftTool: SwiftTool) throws { - let packageDirectory = try globalOptions.locations.packageDirectory ?? swiftTool.getPackageRoot() + func run(_ swiftCommandState: SwiftCommandState) throws { + let packageDirectory = try globalOptions.locations.packageDirectory ?? swiftCommandState.getPackageRoot() let archivePath: AbsolutePath if let output { archivePath = output } else { - let graph = try swiftTool.loadPackageGraph() + let graph = try swiftCommandState.loadPackageGraph() let packageName = graph.rootPackages[graph.rootPackages.startIndex].manifest.displayName // TODO: use identity instead? archivePath = packageDirectory.appending("\(packageName).zip") } - try SwiftPackageTool.archiveSource( + try SwiftPackageCommand.archiveSource( at: packageDirectory, to: archivePath, fileSystem: localFileSystem, - cancellator: swiftTool.cancellator + cancellator: swiftCommandState.cancellator ) if archivePath.isDescendantOfOrEqual(to: packageDirectory) { diff --git a/Sources/Commands/PackageTools/CompletionTool.swift b/Sources/Commands/PackageCommands/CompletionCommand.swift similarity index 84% rename from Sources/Commands/PackageTools/CompletionTool.swift rename to Sources/Commands/PackageCommands/CompletionCommand.swift index d6548e7f146..1326d005383 100644 --- a/Sources/Commands/PackageTools/CompletionTool.swift +++ b/Sources/Commands/PackageCommands/CompletionCommand.swift @@ -15,10 +15,10 @@ import CoreCommands import var TSCBasic.stdoutStream -extension SwiftPackageTool { - struct CompletionTool: SwiftCommand { +extension SwiftPackageCommand { + struct CompletionCommand: SwiftCommand { static let configuration = CommandConfiguration( - abstract: "Completion tool (for shell completions)" + abstract: "Completion command (for shell completions)" ) enum Mode: String, CaseIterable, ExpressibleByArgument { @@ -37,10 +37,10 @@ extension SwiftPackageTool { commandName: "swift", abstract: "The Swift compiler", subcommands: [ - SwiftRunTool.self, - SwiftBuildTool.self, - SwiftTestTool.self, - SwiftPackageTool.self, + SwiftRunCommand.self, + SwiftBuildCommand.self, + SwiftTestCommand.self, + SwiftPackageCommand.self, ] ) } @@ -51,7 +51,7 @@ extension SwiftPackageTool { @Argument(help: "generate-bash-script | generate-zsh-script |\ngenerate-fish-script | list-dependencies | list-executables") var mode: Mode - func run(_ swiftTool: SwiftTool) throws { + func run(_ swiftCommandState: SwiftCommandState) throws { switch mode { case .generateBashScript: let script = SwiftCommand.completionScript(for: .bash) @@ -63,7 +63,7 @@ extension SwiftPackageTool { let script = SwiftCommand.completionScript(for: .fish) print(script) case .listDependencies: - let graph = try swiftTool.loadPackageGraph() + let graph = try swiftCommandState.loadPackageGraph() // command's result output goes on stdout // ie "swift package list-dependencies" should output to stdout ShowDependencies.dumpDependenciesOf( @@ -72,14 +72,14 @@ extension SwiftPackageTool { on: TSCBasic.stdoutStream ) case .listExecutables: - let graph = try swiftTool.loadPackageGraph() + let graph = try swiftCommandState.loadPackageGraph() let package = graph.rootPackages[graph.rootPackages.startIndex].underlying let executables = package.targets.filter { $0.type == .executable } for executable in executables { print(executable.name) } case .listSnippets: - let graph = try swiftTool.loadPackageGraph() + let graph = try swiftCommandState.loadPackageGraph() let package = graph.rootPackages[graph.rootPackages.startIndex].underlying let executables = package.targets.filter { $0.type == .snippet } for executable in executables { diff --git a/Sources/Commands/PackageTools/ComputeChecksum.swift b/Sources/Commands/PackageCommands/ComputeChecksum.swift similarity index 83% rename from Sources/Commands/PackageTools/ComputeChecksum.swift rename to Sources/Commands/PackageCommands/ComputeChecksum.swift index af22f838671..9a0b92e78e0 100644 --- a/Sources/Commands/PackageTools/ComputeChecksum.swift +++ b/Sources/Commands/PackageCommands/ComputeChecksum.swift @@ -27,11 +27,11 @@ struct ComputeChecksum: SwiftCommand { @Argument(help: "The absolute or relative path to the binary artifact") var path: AbsolutePath - func run(_ swiftTool: SwiftTool) throws { + func run(_ swiftCommandState: SwiftCommandState) throws { let binaryArtifactsManager = try Workspace.BinaryArtifactsManager( - fileSystem: swiftTool.fileSystem, - authorizationProvider: swiftTool.getAuthorizationProvider(), - hostToolchain: swiftTool.getHostToolchain(), + fileSystem: swiftCommandState.fileSystem, + authorizationProvider: swiftCommandState.getAuthorizationProvider(), + hostToolchain: swiftCommandState.getHostToolchain(), checksumAlgorithm: SHA256(), cachePath: .none, customHTTPClient: .none, diff --git a/Sources/Commands/PackageTools/Config.swift b/Sources/Commands/PackageCommands/Config.swift similarity index 79% rename from Sources/Commands/PackageTools/Config.swift rename to Sources/Commands/PackageCommands/Config.swift index 2e8d8c72d7d..0d61c59a449 100644 --- a/Sources/Commands/PackageTools/Config.swift +++ b/Sources/Commands/PackageCommands/Config.swift @@ -17,7 +17,7 @@ import Workspace import var TSCBasic.stderrStream -extension SwiftPackageTool { +extension SwiftPackageCommand { struct Config: ParsableCommand { static let configuration = CommandConfiguration( abstract: "Manipulate configuration of the package", @@ -26,7 +26,7 @@ extension SwiftPackageTool { } } -extension SwiftPackageTool.Config { +extension SwiftPackageCommand.Config { struct SetMirror: SwiftCommand { static let configuration = CommandConfiguration( abstract: "Set a mirror for a dependency" @@ -50,32 +50,32 @@ extension SwiftPackageTool.Config { @Option(help: "The mirror url or identity") var mirror: String? - func run(_ swiftTool: SwiftTool) throws { - let config = try getMirrorsConfig(swiftTool) + func run(_ swiftCommandState: SwiftCommandState) throws { + let config = try getMirrorsConfig(swiftCommandState) if self._deprecate_packageURL != nil { - swiftTool.observabilityScope.emit( + swiftCommandState.observabilityScope.emit( warning: "'--package-url' option is deprecated; use '--original' instead" ) } if self._deprecate_originalURL != nil { - swiftTool.observabilityScope.emit( + swiftCommandState.observabilityScope.emit( warning: "'--original-url' option is deprecated; use '--original' instead" ) } if self._deprecate_mirrorURL != nil { - swiftTool.observabilityScope.emit( + swiftCommandState.observabilityScope.emit( warning: "'--mirror-url' option is deprecated; use '--mirror' instead" ) } guard let original = self._deprecate_packageURL ?? self._deprecate_originalURL ?? self.original else { - swiftTool.observabilityScope.emit(.missingRequiredArg("--original")) + swiftCommandState.observabilityScope.emit(.missingRequiredArg("--original")) throw ExitCode.failure } guard let mirror = self._deprecate_mirrorURL ?? self.mirror else { - swiftTool.observabilityScope.emit(.missingRequiredArg("--mirror")) + swiftCommandState.observabilityScope.emit(.missingRequiredArg("--mirror")) throw ExitCode.failure } @@ -108,21 +108,21 @@ extension SwiftPackageTool.Config { @Option(help: "The mirror url or identity") var mirror: String? - func run(_ swiftTool: SwiftTool) throws { - let config = try getMirrorsConfig(swiftTool) + func run(_ swiftCommandState: SwiftCommandState) throws { + let config = try getMirrorsConfig(swiftCommandState) if self._deprecate_packageURL != nil { - swiftTool.observabilityScope.emit( + swiftCommandState.observabilityScope.emit( warning: "'--package-url' option is deprecated; use '--original' instead" ) } if self._deprecate_originalURL != nil { - swiftTool.observabilityScope.emit( + swiftCommandState.observabilityScope.emit( warning: "'--original-url' option is deprecated; use '--original' instead" ) } if self._deprecate_mirrorURL != nil { - swiftTool.observabilityScope.emit( + swiftCommandState.observabilityScope.emit( warning: "'--mirror-url' option is deprecated; use '--mirror' instead" ) } @@ -130,7 +130,7 @@ extension SwiftPackageTool.Config { guard let originalOrMirror = self._deprecate_packageURL ?? self._deprecate_originalURL ?? self .original ?? self._deprecate_mirrorURL ?? self.mirror else { - swiftTool.observabilityScope.emit(.missingRequiredArg("--original or --mirror")) + swiftCommandState.observabilityScope.emit(.missingRequiredArg("--original or --mirror")) throw ExitCode.failure } @@ -156,22 +156,22 @@ extension SwiftPackageTool.Config { @Option(help: "The original url or identity") var original: String? - func run(_ swiftTool: SwiftTool) throws { - let config = try getMirrorsConfig(swiftTool) + func run(_ swiftCommandState: SwiftCommandState) throws { + let config = try getMirrorsConfig(swiftCommandState) if self._deprecate_packageURL != nil { - swiftTool.observabilityScope.emit( + swiftCommandState.observabilityScope.emit( warning: "'--package-url' option is deprecated; use '--original' instead" ) } if self._deprecate_originalURL != nil { - swiftTool.observabilityScope.emit( + swiftCommandState.observabilityScope.emit( warning: "'--original-url' option is deprecated; use '--original' instead" ) } guard let original = self._deprecate_packageURL ?? self._deprecate_originalURL ?? self.original else { - swiftTool.observabilityScope.emit(.missingRequiredArg("--original")) + swiftCommandState.observabilityScope.emit(.missingRequiredArg("--original")) throw ExitCode.failure } @@ -185,10 +185,10 @@ extension SwiftPackageTool.Config { } } - static func getMirrorsConfig(_ swiftTool: SwiftTool) throws -> Workspace.Configuration.Mirrors { - let workspace = try swiftTool.getActiveWorkspace() + static func getMirrorsConfig(_ swiftCommandState: SwiftCommandState) throws -> Workspace.Configuration.Mirrors { + let workspace = try swiftCommandState.getActiveWorkspace() return try .init( - fileSystem: swiftTool.fileSystem, + fileSystem: swiftCommandState.fileSystem, localMirrorsFile: workspace.location.localMirrorsConfigurationFile, sharedMirrorsFile: workspace.location.sharedMirrorsConfigurationFile ) diff --git a/Sources/Commands/PackageTools/Describe.swift b/Sources/Commands/PackageCommands/Describe.swift similarity index 87% rename from Sources/Commands/PackageTools/Describe.swift rename to Sources/Commands/PackageCommands/Describe.swift index 96ec1d5e7aa..ef8001c6e7e 100644 --- a/Sources/Commands/PackageTools/Describe.swift +++ b/Sources/Commands/PackageCommands/Describe.swift @@ -18,7 +18,7 @@ import PackageModel import struct TSCBasic.StringError -extension SwiftPackageTool { +extension SwiftPackageCommand { struct Describe: AsyncSwiftCommand { static let configuration = CommandConfiguration( abstract: "Describe the current package") @@ -29,16 +29,16 @@ extension SwiftPackageTool { @Option(help: "json | text | mermaid") var type: DescribeMode = .text - func run(_ swiftTool: SwiftTool) async throws { - let workspace = try swiftTool.getActiveWorkspace() + func run(_ swiftCommandState: SwiftCommandState) async throws { + let workspace = try swiftCommandState.getActiveWorkspace() - guard let packagePath = try swiftTool.getWorkspaceRoot().packages.first else { + guard let packagePath = try swiftCommandState.getWorkspaceRoot().packages.first else { throw StringError("unknown package") } let package = try await workspace.loadRootPackage( at: packagePath, - observabilityScope: swiftTool.observabilityScope + observabilityScope: swiftCommandState.observabilityScope ) try self.describe(package, in: type) diff --git a/Sources/Commands/PackageTools/DumpCommands.swift b/Sources/Commands/PackageCommands/DumpCommands.swift similarity index 78% rename from Sources/Commands/PackageTools/DumpCommands.swift rename to Sources/Commands/PackageCommands/DumpCommands.swift index 3a569807e70..bde6b0d0fe1 100644 --- a/Sources/Commands/PackageTools/DumpCommands.swift +++ b/Sources/Commands/PackageCommands/DumpCommands.swift @@ -46,18 +46,18 @@ struct DumpSymbolGraph: SwiftCommand { @Flag(help: "Emit extension block symbols for extensions to external types or directly associate members and conformances with the extended nominal.") var extensionBlockSymbolBehavior: ExtensionBlockSymbolBehavior = .omitExtensionBlockSymbols - func run(_ swiftTool: SwiftTool) throws { + func run(_ swiftCommandState: SwiftCommandState) throws { // Build the current package. // // We turn build manifest caching off because we need the build plan. - let buildSystem = try swiftTool.createBuildSystem(explicitBuildSystem: .native, cacheBuildManifest: false) + let buildSystem = try swiftCommandState.createBuildSystem(explicitBuildSystem: .native, cacheBuildManifest: false) try buildSystem.build() // Configure the symbol graph extractor. let symbolGraphExtractor = try SymbolGraphExtract( - fileSystem: swiftTool.fileSystem, - tool: swiftTool.getTargetToolchain().getSymbolGraphExtract(), - observabilityScope: swiftTool.observabilityScope, + fileSystem: swiftCommandState.fileSystem, + tool: swiftCommandState.getTargetToolchain().getSymbolGraphExtract(), + observabilityScope: swiftCommandState.observabilityScope, skipSynthesizedMembers: skipSynthesizedMembers, minimumAccessLevel: minimumAccessLevel, skipInheritedDocs: skipInheritedDocs, @@ -77,16 +77,16 @@ struct DumpSymbolGraph: SwiftCommand { buildPlan: buildPlan, outputRedirection: .collect(redirectStderr: true), outputDirectory: symbolGraphDirectory, - verboseOutput: swiftTool.logLevel <= .info + verboseOutput: swiftCommandState.logLevel <= .info ) if result.exitStatus != .terminated(code: 0) { let commandline = "\nUsing commandline: \(result.arguments)" switch result.output { case .success(let value): - swiftTool.observabilityScope.emit(error: "Failed to emit symbol graph for '\(target.c99name)': \(String(decoding: value, as: UTF8.self))\(commandline)") + swiftCommandState.observabilityScope.emit(error: "Failed to emit symbol graph for '\(target.c99name)': \(String(decoding: value, as: UTF8.self))\(commandline)") case .failure(let error): - swiftTool.observabilityScope.emit(error: "Internal error while emitting symbol graph for '\(target.c99name)': \(error)\(commandline)") + swiftCommandState.observabilityScope.emit(error: "Internal error while emitting symbol graph for '\(target.c99name)': \(error)\(commandline)") } } } @@ -107,13 +107,13 @@ struct DumpPackage: AsyncSwiftCommand { @OptionGroup(visibility: .hidden) var globalOptions: GlobalOptions - func run(_ swiftTool: SwiftTool) async throws { - let workspace = try swiftTool.getActiveWorkspace() - let root = try swiftTool.getWorkspaceRoot() + func run(_ swiftCommandState: SwiftCommandState) async throws { + let workspace = try swiftCommandState.getActiveWorkspace() + let root = try swiftCommandState.getWorkspaceRoot() let rootManifests = try await workspace.loadRootManifests( packages: root.packages, - observabilityScope: swiftTool.observabilityScope + observabilityScope: swiftCommandState.observabilityScope ) guard let rootManifest = rootManifests.values.first else { throw StringError("invalid manifests at \(root.packages)") @@ -129,7 +129,7 @@ struct DumpPackage: AsyncSwiftCommand { } struct DumpPIF: SwiftCommand { - // hides this command from CLI --help output + // hides this command from CLI `--help` output static let configuration = CommandConfiguration(shouldDisplay: false) @OptionGroup(visibility: .hidden) @@ -138,14 +138,14 @@ struct DumpPIF: SwiftCommand { @Flag(help: "Preserve the internal structure of PIF") var preserveStructure: Bool = false - func run(_ swiftTool: SwiftTool) throws { + func run(_ swiftCommandState: SwiftCommandState) throws { #if !DISABLE_XCBUILD_SUPPORT - let graph = try swiftTool.loadPackageGraph() + let graph = try swiftCommandState.loadPackageGraph() let pif = try PIFBuilder.generatePIF( - buildParameters: swiftTool.productsBuildParameters, + buildParameters: swiftCommandState.productsBuildParameters, packageGraph: graph, - fileSystem: swiftTool.fileSystem, - observabilityScope: swiftTool.observabilityScope, + fileSystem: swiftCommandState.fileSystem, + observabilityScope: swiftCommandState.observabilityScope, preservePIFModelStructure: preserveStructure) print(pif) #else diff --git a/Sources/Commands/PackageTools/EditCommands.swift b/Sources/Commands/PackageCommands/EditCommands.swift similarity index 75% rename from Sources/Commands/PackageTools/EditCommands.swift rename to Sources/Commands/PackageCommands/EditCommands.swift index 555c3a5abb8..42195f7037d 100644 --- a/Sources/Commands/PackageTools/EditCommands.swift +++ b/Sources/Commands/PackageCommands/EditCommands.swift @@ -15,7 +15,7 @@ import Basics import CoreCommands import SourceControl -extension SwiftPackageTool { +extension SwiftPackageCommand { struct Edit: SwiftCommand { static let configuration = CommandConfiguration( abstract: "Put a package in editable mode") @@ -35,9 +35,9 @@ extension SwiftPackageTool { @Argument(help: "The name of the package to edit") var packageName: String - func run(_ swiftTool: SwiftTool) throws { - try swiftTool.resolve() - let workspace = try swiftTool.getActiveWorkspace() + func run(_ swiftCommandState: SwiftCommandState) throws { + try swiftCommandState.resolve() + let workspace = try swiftCommandState.getActiveWorkspace() // Put the dependency in edit mode. workspace.edit( @@ -45,7 +45,7 @@ extension SwiftPackageTool { path: path, revision: revision, checkoutBranch: checkoutBranch, - observabilityScope: swiftTool.observabilityScope + observabilityScope: swiftCommandState.observabilityScope ) } } @@ -64,16 +64,16 @@ extension SwiftPackageTool { @Argument(help: "The name of the package to unedit") var packageName: String - func run(_ swiftTool: SwiftTool) throws { - try swiftTool.resolve() - let workspace = try swiftTool.getActiveWorkspace() + func run(_ swiftCommandState: SwiftCommandState) throws { + try swiftCommandState.resolve() + let workspace = try swiftCommandState.getActiveWorkspace() try workspace.unedit( packageName: packageName, forceRemove: shouldForceRemove, - root: swiftTool.getWorkspaceRoot(), - availableLibraries: swiftTool.getHostToolchain().providedLibraries, - observabilityScope: swiftTool.observabilityScope + root: swiftCommandState.getWorkspaceRoot(), + availableLibraries: swiftCommandState.getHostToolchain().providedLibraries, + observabilityScope: swiftCommandState.observabilityScope ) } } diff --git a/Sources/Commands/PackageTools/Format.swift b/Sources/Commands/PackageCommands/Format.swift similarity index 85% rename from Sources/Commands/PackageTools/Format.swift rename to Sources/Commands/PackageCommands/Format.swift index 3ba0ccc7dc0..f4bfdce769a 100644 --- a/Sources/Commands/PackageTools/Format.swift +++ b/Sources/Commands/PackageCommands/Format.swift @@ -20,7 +20,7 @@ import enum TSCBasic.ProcessEnv import enum TSCUtility.Diagnostics -extension SwiftPackageTool { +extension SwiftPackageCommand { struct Format: AsyncSwiftCommand { static let configuration = CommandConfiguration( commandName: "_format", shouldDisplay: false) @@ -32,25 +32,25 @@ extension SwiftPackageTool { help: "Pass flag through to the swift-format tool") var swiftFormatFlags: [String] = [] - func run(_ swiftTool: SwiftTool) async throws { + func run(_ swiftCommandState: SwiftCommandState) async throws { // Look for swift-format binary. // FIXME: This should be moved to user toolchain. let swiftFormatInEnv = lookupExecutablePath(filename: ProcessEnv.vars["SWIFT_FORMAT"]) guard let swiftFormat = swiftFormatInEnv ?? Process.findExecutable("swift-format").flatMap(AbsolutePath.init) else { - swiftTool.observabilityScope.emit(error: "Could not find swift-format in PATH or SWIFT_FORMAT") + swiftCommandState.observabilityScope.emit(error: "Could not find swift-format in PATH or SWIFT_FORMAT") throw TSCUtility.Diagnostics.fatalError } // Get the root package. - let workspace = try swiftTool.getActiveWorkspace() + let workspace = try swiftCommandState.getActiveWorkspace() - guard let packagePath = try swiftTool.getWorkspaceRoot().packages.first else { + guard let packagePath = try swiftCommandState.getWorkspaceRoot().packages.first else { throw StringError("unknown package") } let package = try await workspace.loadRootPackage( at: packagePath, - observabilityScope: swiftTool.observabilityScope + observabilityScope: swiftCommandState.observabilityScope ) diff --git a/Sources/Commands/PackageTools/Init.swift b/Sources/Commands/PackageCommands/Init.swift similarity index 90% rename from Sources/Commands/PackageTools/Init.swift rename to Sources/Commands/PackageCommands/Init.swift index 444ddce142a..a8555b00f0a 100644 --- a/Sources/Commands/PackageTools/Init.swift +++ b/Sources/Commands/PackageCommands/Init.swift @@ -16,7 +16,7 @@ import CoreCommands import Workspace import SPMBuildCore -extension SwiftPackageTool { +extension SwiftPackageCommand { struct Init: SwiftCommand { public static let configuration = CommandConfiguration( abstract: "Initialize a new package") @@ -54,8 +54,8 @@ extension SwiftPackageTool { @Option(name: .customLong("name"), help: "Provide custom package name") var packageName: String? - func run(_ swiftTool: SwiftTool) throws { - guard let cwd = swiftTool.fileSystem.currentWorkingDirectory else { + func run(_ swiftCommandState: SwiftCommandState) throws { + guard let cwd = swiftCommandState.fileSystem.currentWorkingDirectory else { throw InternalError("Could not find the current working directory") } @@ -72,8 +72,8 @@ extension SwiftPackageTool { packageType: initMode, supportedTestingLibraries: testingLibraries, destinationPath: cwd, - installedSwiftPMConfiguration: swiftTool.getHostToolchain().installedSwiftPMConfiguration, - fileSystem: swiftTool.fileSystem + installedSwiftPMConfiguration: swiftCommandState.getHostToolchain().installedSwiftPMConfiguration, + fileSystem: swiftCommandState.fileSystem ) initPackage.progressReporter = { message in print(message) diff --git a/Sources/Commands/PackageTools/InstalledPackages.swift b/Sources/Commands/PackageCommands/InstalledPackages.swift similarity index 97% rename from Sources/Commands/PackageTools/InstalledPackages.swift rename to Sources/Commands/PackageCommands/InstalledPackages.swift index c473701479a..0ad9603eb7b 100644 --- a/Sources/Commands/PackageTools/InstalledPackages.swift +++ b/Sources/Commands/PackageCommands/InstalledPackages.swift @@ -16,7 +16,7 @@ import Foundation import PackageModel import TSCBasic -extension SwiftPackageTool { +extension SwiftPackageCommand { struct Install: SwiftCommand { static let configuration = CommandConfiguration( commandName: "experimental-install", @@ -29,7 +29,7 @@ extension SwiftPackageTool { @Option(help: "The name of the executable product to install") var product: String? - func run(_ tool: SwiftTool) throws { + func run(_ tool: SwiftCommandState) throws { let swiftpmBinDir = try tool.fileSystem.getOrCreateSwiftPMInstalledBinariesDirectory() let env = ProcessInfo.processInfo.environment @@ -103,7 +103,7 @@ extension SwiftPackageTool { @Argument(help: "Name of the executable to uninstall.") var name: String - func run(_ tool: SwiftTool) throws { + func run(_ tool: SwiftCommandState) throws { let alreadyInstalled = (try? InstalledPackageProduct.installedProducts(tool.fileSystem)) ?? [] guard let removedExecutable = alreadyInstalled.first(where: { $0.name == name }) else { diff --git a/Sources/Commands/PackageTools/Learn.swift b/Sources/Commands/PackageCommands/Learn.swift similarity index 93% rename from Sources/Commands/PackageTools/Learn.swift rename to Sources/Commands/PackageCommands/Learn.swift index 8e4af0d9410..3632c297d6e 100644 --- a/Sources/Commands/PackageTools/Learn.swift +++ b/Sources/Commands/PackageCommands/Learn.swift @@ -16,7 +16,7 @@ import CoreCommands import PackageGraph import PackageModel -extension SwiftPackageTool { +extension SwiftPackageCommand { struct Learn: SwiftCommand { @OptionGroup() @@ -90,14 +90,14 @@ extension SwiftPackageTool { return snippetGroups.filter { !$0.snippets.isEmpty } } - func run(_ swiftTool: SwiftTool) throws { - let graph = try swiftTool.loadPackageGraph() + func run(_ swiftCommandState: SwiftCommandState) throws { + let graph = try swiftCommandState.loadPackageGraph() let package = graph.rootPackages[graph.rootPackages.startIndex] print(package.products.map { $0.description }) - let snippetGroups = try loadSnippetsAndSnippetGroups(fileSystem: swiftTool.fileSystem, from: package) + let snippetGroups = try loadSnippetsAndSnippetGroups(fileSystem: swiftCommandState.fileSystem, from: package) - var cardStack = CardStack(package: package, snippetGroups: snippetGroups, swiftTool: swiftTool) + var cardStack = CardStack(package: package, snippetGroups: snippetGroups, swiftCommandState: swiftCommandState) cardStack.run() } diff --git a/Sources/Commands/PackageTools/PluginCommand.swift b/Sources/Commands/PackageCommands/PluginCommand.swift similarity index 91% rename from Sources/Commands/PackageTools/PluginCommand.swift rename to Sources/Commands/PackageCommands/PluginCommand.swift index e1df26adaba..2fac7c1498d 100644 --- a/Sources/Commands/PackageTools/PluginCommand.swift +++ b/Sources/Commands/PackageCommands/PluginCommand.swift @@ -137,7 +137,7 @@ struct PluginCommand: SwiftCommand { ) var arguments: [String] = [] - func run(_ swiftTool: SwiftTool) throws { + func run(_ swiftCommandState: SwiftCommandState) throws { // Check for a missing plugin command verb. if self.command == "" && !self.listCommands { throw ValidationError("Missing expected plugin command") @@ -145,7 +145,7 @@ struct PluginCommand: SwiftCommand { // List the available plugins, if asked to. if self.listCommands { - let packageGraph = try swiftTool.loadPackageGraph() + let packageGraph = try swiftCommandState.loadPackageGraph() let allPlugins = PluginCommand.availableCommandPlugins(in: packageGraph, limitedTo: self.pluginOptions.packageIdentity) for plugin in allPlugins.sorted(by: { $0.name < $1.name }) { guard case .command(let intent, _) = plugin.capability else { continue } @@ -165,7 +165,7 @@ struct PluginCommand: SwiftCommand { command: self.command, options: self.pluginOptions, arguments: self.arguments, - swiftTool: swiftTool + swiftCommandState: swiftCommandState ) } @@ -173,12 +173,12 @@ struct PluginCommand: SwiftCommand { command: String, options: PluginOptions, arguments: [String], - swiftTool: SwiftTool + swiftCommandState: SwiftCommandState ) throws { // Load the workspace and resolve the package graph. - let packageGraph = try swiftTool.loadPackageGraph() + let packageGraph = try swiftCommandState.loadPackageGraph() - swiftTool.observabilityScope.emit(info: "Finding plugin for command ‘\(command)’") + swiftCommandState.observabilityScope.emit(info: "Finding plugin for command ‘\(command)’") let matchingPlugins = PluginCommand.findPlugins(matching: command, in: packageGraph, limitedTo: options.packageIdentity) // Complain if we didn't find exactly one. @@ -194,7 +194,7 @@ struct PluginCommand: SwiftCommand { // merge the relevant plugin execution options let pluginOptions = options.merged(with: pluginArguments.pluginOptions) // sandbox is special since its generic not a specific plugin option - swiftTool.shouldDisableSandbox = swiftTool.shouldDisableSandbox || pluginArguments.globalOptions.security + swiftCommandState.shouldDisableSandbox = swiftCommandState.shouldDisableSandbox || pluginArguments.globalOptions.security .shouldDisableSandbox // At this point we know we found exactly one command plugin, so we run it. In SwiftPM CLI, we have only one root package. @@ -204,7 +204,7 @@ struct PluginCommand: SwiftCommand { packageGraph: packageGraph, options: pluginOptions, arguments: unparsedArguments, - swiftTool: swiftTool + swiftCommandState: swiftCommandState ) } @@ -214,19 +214,19 @@ struct PluginCommand: SwiftCommand { packageGraph: ModulesGraph, options: PluginOptions, arguments: [String], - swiftTool: SwiftTool + swiftCommandState: SwiftCommandState ) throws { - swiftTool.observabilityScope + swiftCommandState.observabilityScope .emit( info: "Running command plugin \(plugin) on package \(package) with options \(options) and arguments \(arguments)" ) // The `plugins` directory is inside the workspace's main data directory, and contains all temporary files related to this plugin in the workspace. - let pluginsDir = try swiftTool.getActiveWorkspace().location.pluginWorkingDirectory + let pluginsDir = try swiftCommandState.getActiveWorkspace().location.pluginWorkingDirectory .appending(component: plugin.name) // The `cache` directory is in the plugin’s directory and is where the plugin script runner caches compiled plugin binaries and any other derived information for this plugin. - let pluginScriptRunner = try swiftTool.getPluginScriptRunner( + let pluginScriptRunner = try swiftCommandState.getPluginScriptRunner( customPluginsDir: pluginsDir ) @@ -276,11 +276,11 @@ struct PluginCommand: SwiftCommand { let problem = "Plugin ‘\(plugin.name)’ wants permission to \(permissionString)." let reason = "Stated reason: “\(reasonString)”." - if swiftTool.outputStream.isTTY { + if swiftCommandState.outputStream.isTTY { // We can ask the user directly, so we do so. let query = "Allow this plugin to \(permissionString)?" - swiftTool.outputStream.write("\(problem)\n\(reason)\n\(query) (yes/no) ".utf8) - swiftTool.outputStream.flush() + swiftCommandState.outputStream.write("\(problem)\n\(reason)\n\(query) (yes/no) ".utf8) + swiftCommandState.outputStream.flush() let answer = readLine(strippingNewline: true) // Throw an error if we didn't get permission. if answer?.lowercased() != "yes" { @@ -304,7 +304,7 @@ struct PluginCommand: SwiftCommand { for pathString in options.additionalAllowedWritableDirectories { writableDirectories - .append(try AbsolutePath(validating: pathString, relativeTo: swiftTool.originalWorkingDirectory)) + .append(try AbsolutePath(validating: pathString, relativeTo: swiftCommandState.originalWorkingDirectory)) } // Make sure that the package path is read-only unless it's covered by any of the explicitly writable directories. @@ -312,12 +312,12 @@ struct PluginCommand: SwiftCommand { .contains { package.path.isDescendantOfOrEqual(to: $0) } ? [] : [package.path] // Use the directory containing the compiler as an additional search directory, and add the $PATH. - let toolSearchDirs = [try swiftTool.getTargetToolchain().swiftCompilerPath.parentDirectory] + let toolSearchDirs = [try swiftCommandState.getTargetToolchain().swiftCompilerPath.parentDirectory] + getEnvSearchPaths(pathString: ProcessEnv.path, currentWorkingDirectory: .none) - let buildParameters = try swiftTool.toolsBuildParameters + let buildParameters = try swiftCommandState.toolsBuildParameters // Build or bring up-to-date any executable host-side tools on which this plugin depends. Add them and any binary dependencies to the tool-names-to-path map. - let buildSystem = try swiftTool.createBuildSystem( + let buildSystem = try swiftCommandState.createBuildSystem( explicitBuildSystem: .native, cacheBuildManifest: false, // Force all dependencies to be built for the host, to work around the fact that BuildOperation.plan @@ -327,7 +327,7 @@ struct PluginCommand: SwiftCommand { ) let accessibleTools = try plugin.processAccessibleTools( packageGraph: packageGraph, - fileSystem: swiftTool.fileSystem, + fileSystem: swiftCommandState.fileSystem, environment: buildParameters.buildEnvironment, for: try pluginScriptRunner.hostTriple ) { name, _ in @@ -341,7 +341,7 @@ struct PluginCommand: SwiftCommand { } // Set up a delegate to handle callbacks from the command plugin. - let pluginDelegate = PluginDelegate(swiftTool: swiftTool, plugin: plugin) + let pluginDelegate = PluginDelegate(swiftCommandState: swiftCommandState, plugin: plugin) let delegateQueue = DispatchQueue(label: "plugin-invocation") // Run the command plugin. @@ -350,17 +350,17 @@ struct PluginCommand: SwiftCommand { action: .performCommand(package: package, arguments: arguments), buildEnvironment: buildEnvironment, scriptRunner: pluginScriptRunner, - workingDirectory: swiftTool.originalWorkingDirectory, + workingDirectory: swiftCommandState.originalWorkingDirectory, outputDirectory: outputDir, toolSearchDirectories: toolSearchDirs, accessibleTools: accessibleTools, writableDirectories: writableDirectories, readOnlyDirectories: readOnlyDirectories, allowNetworkConnections: allowNetworkConnections, - pkgConfigDirectories: swiftTool.options.locations.pkgConfigDirectories, + pkgConfigDirectories: swiftCommandState.options.locations.pkgConfigDirectories, sdkRootPath: buildParameters.toolchain.sdkRootPath, - fileSystem: swiftTool.fileSystem, - observabilityScope: swiftTool.observabilityScope, + fileSystem: swiftCommandState.fileSystem, + observabilityScope: swiftCommandState.observabilityScope, callbackQueue: delegateQueue, delegate: pluginDelegate, completion: $0 diff --git a/Sources/Commands/PackageTools/ResetCommands.swift b/Sources/Commands/PackageCommands/ResetCommands.swift similarity index 68% rename from Sources/Commands/PackageTools/ResetCommands.swift rename to Sources/Commands/PackageCommands/ResetCommands.swift index 26204d1f49a..fd2a872c228 100644 --- a/Sources/Commands/PackageTools/ResetCommands.swift +++ b/Sources/Commands/PackageCommands/ResetCommands.swift @@ -13,7 +13,7 @@ import ArgumentParser import CoreCommands -extension SwiftPackageTool { +extension SwiftPackageCommand { struct Clean: SwiftCommand { static let configuration = CommandConfiguration( abstract: "Delete build artifacts") @@ -21,8 +21,8 @@ extension SwiftPackageTool { @OptionGroup(visibility: .hidden) var globalOptions: GlobalOptions - func run(_ swiftTool: SwiftTool) throws { - try swiftTool.getActiveWorkspace().clean(observabilityScope: swiftTool.observabilityScope) + func run(_ swiftCommandState: SwiftCommandState) throws { + try swiftCommandState.getActiveWorkspace().clean(observabilityScope: swiftCommandState.observabilityScope) } } @@ -33,8 +33,8 @@ extension SwiftPackageTool { @OptionGroup(visibility: .hidden) var globalOptions: GlobalOptions - func run(_ swiftTool: SwiftTool) throws { - try swiftTool.getActiveWorkspace().purgeCache(observabilityScope: swiftTool.observabilityScope) + func run(_ swiftCommandState: SwiftCommandState) throws { + try swiftCommandState.getActiveWorkspace().purgeCache(observabilityScope: swiftCommandState.observabilityScope) } } @@ -45,8 +45,8 @@ extension SwiftPackageTool { @OptionGroup(visibility: .hidden) var globalOptions: GlobalOptions - func run(_ swiftTool: SwiftTool) throws { - try swiftTool.getActiveWorkspace().reset(observabilityScope: swiftTool.observabilityScope) + func run(_ swiftCommandState: SwiftCommandState) throws { + try swiftCommandState.getActiveWorkspace().reset(observabilityScope: swiftCommandState.observabilityScope) } } } diff --git a/Sources/Commands/PackageTools/Resolve.swift b/Sources/Commands/PackageCommands/Resolve.swift similarity index 76% rename from Sources/Commands/PackageTools/Resolve.swift rename to Sources/Commands/PackageCommands/Resolve.swift index f89faf0f6bf..2107e60bc67 100644 --- a/Sources/Commands/PackageTools/Resolve.swift +++ b/Sources/Commands/PackageCommands/Resolve.swift @@ -14,7 +14,7 @@ import ArgumentParser import CoreCommands import TSCUtility -extension SwiftPackageTool { +extension SwiftPackageCommand { struct ResolveOptions: ParsableArguments { @Option(help: "The version to resolve at", transform: { Version($0) }) var version: Version? @@ -39,24 +39,24 @@ extension SwiftPackageTool { @OptionGroup() var resolveOptions: ResolveOptions - func run(_ swiftTool: SwiftTool) throws { + func run(_ swiftCommandState: SwiftCommandState) throws { // If a package is provided, use that to resolve the dependencies. if let packageName = resolveOptions.packageName { - let workspace = try swiftTool.getActiveWorkspace() + let workspace = try swiftCommandState.getActiveWorkspace() try workspace.resolve( packageName: packageName, - root: swiftTool.getWorkspaceRoot(), + root: swiftCommandState.getWorkspaceRoot(), version: resolveOptions.version, branch: resolveOptions.branch, revision: resolveOptions.revision, - observabilityScope: swiftTool.observabilityScope + observabilityScope: swiftCommandState.observabilityScope ) - if swiftTool.observabilityScope.errorsReported { + if swiftCommandState.observabilityScope.errorsReported { throw ExitCode.failure } } else { // Otherwise, run a normal resolve. - try swiftTool.resolve() + try swiftCommandState.resolve() } } } @@ -70,11 +70,11 @@ extension SwiftPackageTool { @OptionGroup() var resolveOptions: ResolveOptions - func run(_ swiftTool: SwiftTool) throws { - swiftTool.observabilityScope.emit(warning: "'fetch' command is deprecated; use 'resolve' instead") + func run(_ swiftCommandState: SwiftCommandState) throws { + swiftCommandState.observabilityScope.emit(warning: "'fetch' command is deprecated; use 'resolve' instead") let resolveCommand = Resolve(globalOptions: _globalOptions, resolveOptions: _resolveOptions) - try resolveCommand.run(swiftTool) + try resolveCommand.run(swiftCommandState) } } } diff --git a/Sources/Commands/PackageTools/ShowDependencies.swift b/Sources/Commands/PackageCommands/ShowDependencies.swift similarity index 95% rename from Sources/Commands/PackageTools/ShowDependencies.swift rename to Sources/Commands/PackageCommands/ShowDependencies.swift index 9818bed91b0..1572c6e0f63 100644 --- a/Sources/Commands/PackageTools/ShowDependencies.swift +++ b/Sources/Commands/PackageCommands/ShowDependencies.swift @@ -20,7 +20,7 @@ import class TSCBasic.LocalFileOutputByteStream import protocol TSCBasic.OutputByteStream import var TSCBasic.stdoutStream -extension SwiftPackageTool { +extension SwiftPackageCommand { struct ShowDependencies: SwiftCommand { static let configuration = CommandConfiguration( abstract: "Print the resolved dependency graph") @@ -35,8 +35,8 @@ extension SwiftPackageTool { help: "The absolute or relative path to output the resolved dependency graph.") var outputPath: AbsolutePath? - func run(_ swiftTool: SwiftTool) throws { - let graph = try swiftTool.loadPackageGraph() + func run(_ swiftCommandState: SwiftCommandState) throws { + let graph = try swiftCommandState.loadPackageGraph() // command's result output goes on stdout // ie "swift package show-dependencies" should output to stdout let stream: OutputByteStream = try outputPath.map { try LocalFileOutputByteStream($0) } ?? TSCBasic.stdoutStream diff --git a/Sources/Commands/PackageTools/SwiftPackageTool.swift b/Sources/Commands/PackageCommands/SwiftPackageCommand.swift similarity index 93% rename from Sources/Commands/PackageTools/SwiftPackageTool.swift rename to Sources/Commands/PackageCommands/SwiftPackageCommand.swift index ececa15cf65..826e22912b0 100644 --- a/Sources/Commands/PackageTools/SwiftPackageTool.swift +++ b/Sources/Commands/PackageCommands/SwiftPackageCommand.swift @@ -28,7 +28,7 @@ import XCBuildSupport import enum TSCUtility.Diagnostics /// swift-package tool namespace -public struct SwiftPackageTool: AsyncParsableCommand { +public struct SwiftPackageCommand: AsyncParsableCommand { public static var configuration = CommandConfiguration( commandName: "package", _superCommandName: "swift", @@ -64,7 +64,7 @@ public struct SwiftPackageTool: AsyncParsableCommand { ToolsVersionCommand.self, ComputeChecksum.self, ArchiveSource.self, - CompletionTool.self, + CompletionCommand.self, PluginCommand.self, DefaultCommand.self, @@ -82,7 +82,7 @@ public struct SwiftPackageTool: AsyncParsableCommand { public init() {} } -extension SwiftPackageTool { +extension SwiftPackageCommand { // This command is the default when no other subcommand is passed. It is not shown in the help and is never invoked // directly. struct DefaultCommand: SwiftCommand { @@ -100,10 +100,10 @@ extension SwiftPackageTool { @Argument(parsing: .captureForPassthrough) var remaining: [String] = [] - func run(_ swiftTool: SwiftTool) throws { + func run(_ swiftCommandState: SwiftCommandState) throws { // See if have a possible plugin command. guard let command = remaining.first else { - print(SwiftPackageTool.helpMessage()) + print(SwiftPackageCommand.helpMessage()) return } @@ -119,7 +119,7 @@ extension SwiftPackageTool { command: command, options: self.pluginOptions, arguments: self.remaining, - swiftTool: swiftTool + swiftCommandState: swiftCommandState ) } } diff --git a/Sources/Commands/PackageTools/ToolsVersionCommand.swift b/Sources/Commands/PackageCommands/ToolsVersionCommand.swift similarity index 87% rename from Sources/Commands/PackageTools/ToolsVersionCommand.swift rename to Sources/Commands/PackageCommands/ToolsVersionCommand.swift index 951ac0947fb..035a47ef7f4 100644 --- a/Sources/Commands/PackageTools/ToolsVersionCommand.swift +++ b/Sources/Commands/PackageCommands/ToolsVersionCommand.swift @@ -49,13 +49,13 @@ struct ToolsVersionCommand: SwiftCommand { } } - func run(_ swiftTool: SwiftTool) throws { - let pkg = try swiftTool.getPackageRoot() + func run(_ swiftCommandState: SwiftCommandState) throws { + let pkg = try swiftCommandState.getPackageRoot() switch toolsVersionMode { case .display: - let manifestPath = try ManifestLoader.findManifest(packagePath: pkg, fileSystem: swiftTool.fileSystem, currentToolsVersion: .current) - let version = try ToolsVersionParser.parse(manifestPath: manifestPath, fileSystem: swiftTool.fileSystem) + let manifestPath = try ManifestLoader.findManifest(packagePath: pkg, fileSystem: swiftCommandState.fileSystem, currentToolsVersion: .current) + let version = try ToolsVersionParser.parse(manifestPath: manifestPath, fileSystem: swiftCommandState.fileSystem) print("\(version)") case .set(let value): @@ -66,7 +66,7 @@ struct ToolsVersionCommand: SwiftCommand { try ToolsVersionSpecificationWriter.rewriteSpecification( manifestDirectory: pkg, toolsVersion: toolsVersion, - fileSystem: swiftTool.fileSystem + fileSystem: swiftCommandState.fileSystem ) case .setCurrent: @@ -76,7 +76,7 @@ struct ToolsVersionCommand: SwiftCommand { try ToolsVersionSpecificationWriter.rewriteSpecification( manifestDirectory: pkg, toolsVersion: ToolsVersion.current.zeroedPatch, - fileSystem: swiftTool.fileSystem + fileSystem: swiftCommandState.fileSystem ) } } diff --git a/Sources/Commands/PackageTools/Update.swift b/Sources/Commands/PackageCommands/Update.swift similarity index 86% rename from Sources/Commands/PackageTools/Update.swift rename to Sources/Commands/PackageCommands/Update.swift index 395d34d6ce6..50a325ab660 100644 --- a/Sources/Commands/PackageTools/Update.swift +++ b/Sources/Commands/PackageCommands/Update.swift @@ -17,7 +17,7 @@ import PackageModel import PackageGraph import Workspace -extension SwiftPackageTool { +extension SwiftPackageCommand { struct Update: SwiftCommand { static let configuration = CommandConfiguration( abstract: "Update package dependencies") @@ -32,24 +32,24 @@ extension SwiftPackageTool { @Argument(help: "The packages to update") var packages: [String] = [] - func run(_ swiftTool: SwiftTool) throws { - let workspace = try swiftTool.getActiveWorkspace() + func run(_ swiftCommandState: SwiftCommandState) throws { + let workspace = try swiftCommandState.getActiveWorkspace() let changes = try workspace.updateDependencies( - root: swiftTool.getWorkspaceRoot(), + root: swiftCommandState.getWorkspaceRoot(), packages: packages, dryRun: dryRun, - observabilityScope: swiftTool.observabilityScope + observabilityScope: swiftCommandState.observabilityScope ) - if self.dryRun, let changes = changes, let pinsStore = swiftTool.observabilityScope.trap({ try workspace.pinsStore.load() }){ + if self.dryRun, let changes = changes, let pinsStore = swiftCommandState.observabilityScope.trap({ try workspace.pinsStore.load() }){ self.logPackageChanges(changes: changes, pins: pinsStore) } if !self.dryRun { // Throw if there were errors when loading the graph. // The actual errors will be printed before exiting. - guard !swiftTool.observabilityScope.errorsReported else { + guard !swiftCommandState.observabilityScope.errorsReported else { throw ExitCode.failure } } diff --git a/Sources/Commands/Snippets/CardStack.swift b/Sources/Commands/Snippets/CardStack.swift index 7d2f3cb52d7..72d9c5d0c5b 100644 --- a/Sources/Commands/Snippets/CardStack.swift +++ b/Sources/Commands/Snippets/CardStack.swift @@ -34,17 +34,17 @@ struct CardStack { var cards = [Card]() /// The tool used for eventually building and running a chosen snippet. - var swiftTool: SwiftTool + var swiftCommandState: SwiftCommandState /// When true, the escape sequence for clearing the terminal should be /// printed first. private var needsToClearScreen = true - init(package: ResolvedPackage, snippetGroups: [SnippetGroup], swiftTool: SwiftTool) { + init(package: ResolvedPackage, snippetGroups: [SnippetGroup], swiftCommandState: SwiftCommandState) { // this interaction is done on stdout self.terminal = TerminalController(stream: TSCBasic.stdoutStream)! - self.cards = [TopCard(package: package, snippetGroups: snippetGroups, swiftTool: swiftTool)] - self.swiftTool = swiftTool + self.cards = [TopCard(package: package, snippetGroups: snippetGroups, swiftCommandState: swiftCommandState)] + self.swiftCommandState = swiftCommandState } mutating func push(_ card: Card) { @@ -102,7 +102,7 @@ struct CardStack { case let .pop(error): cards.removeLast() if let error { - self.swiftTool.observabilityScope.emit(error) + self.swiftCommandState.observabilityScope.emit(error) needsToClearScreen = false } else { needsToClearScreen = !cards.isEmpty diff --git a/Sources/Commands/Snippets/Cards/SnippetCard.swift b/Sources/Commands/Snippets/Cards/SnippetCard.swift index 8d5492e9416..72f1afa1017 100644 --- a/Sources/Commands/Snippets/Cards/SnippetCard.swift +++ b/Sources/Commands/Snippets/Cards/SnippetCard.swift @@ -36,7 +36,7 @@ struct SnippetCard: Card { var number: Int /// The tool used for eventually building and running a chosen snippet. - var swiftTool: SwiftTool + var swiftCommandState: SwiftCommandState func render() -> String { var rendered = colorized { @@ -93,9 +93,9 @@ struct SnippetCard: Card { func runExample() throws { print("Building '\(snippet.path)'\n") - let buildSystem = try swiftTool.createBuildSystem(explicitProduct: snippet.name) + let buildSystem = try swiftCommandState.createBuildSystem(explicitProduct: snippet.name) try buildSystem.build(subset: .product(snippet.name)) - let executablePath = try swiftTool.productsBuildParameters.buildPath.appending(component: snippet.name) + let executablePath = try swiftCommandState.productsBuildParameters.buildPath.appending(component: snippet.name) if let exampleTarget = try buildSystem.getPackageGraph().allTargets.first(where: { $0.name == snippet.name }) { try ProcessEnv.chdir(exampleTarget.sources.paths[0].parentDirectory) } diff --git a/Sources/Commands/Snippets/Cards/SnippetGroupCard.swift b/Sources/Commands/Snippets/Cards/SnippetGroupCard.swift index 220246ad438..2636d31d586 100644 --- a/Sources/Commands/Snippets/Cards/SnippetGroupCard.swift +++ b/Sources/Commands/Snippets/Cards/SnippetGroupCard.swift @@ -19,7 +19,7 @@ struct SnippetGroupCard: Card { var snippetGroup: SnippetGroup /// The tool used for eventually building and running a chosen snippet. - var swiftTool: SwiftTool + var swiftCommandState: SwiftCommandState var inputPrompt: String? { return """ @@ -39,9 +39,9 @@ struct SnippetGroupCard: Card { } if let index = Int(line), snippetGroup.snippets.indices.contains(index) { - return .push(SnippetCard(snippet: snippetGroup.snippets[index], number: index, swiftTool: swiftTool)) + return .push(SnippetCard(snippet: snippetGroup.snippets[index], number: index, swiftCommandState: swiftCommandState)) } else if let foundSnippetIndex = snippetGroup.snippets.firstIndex(where: { $0.name == line }) { - return .push(SnippetCard(snippet: snippetGroup.snippets[foundSnippetIndex], number: foundSnippetIndex, swiftTool: swiftTool)) + return .push(SnippetCard(snippet: snippetGroup.snippets[foundSnippetIndex], number: foundSnippetIndex, swiftCommandState: swiftCommandState)) } else { print(red { "There is not a snippet by that name or index." }) return nil diff --git a/Sources/Commands/Snippets/Cards/TopCard.swift b/Sources/Commands/Snippets/Cards/TopCard.swift index 6a101f9778e..4b7ed38a418 100644 --- a/Sources/Commands/Snippets/Cards/TopCard.swift +++ b/Sources/Commands/Snippets/Cards/TopCard.swift @@ -24,12 +24,12 @@ struct TopCard: Card { let snippetGroups: [SnippetGroup] /// The tool used for eventually building and running a chosen snippet. - let swiftTool: SwiftTool + let swiftCommandState: SwiftCommandState - init(package: ResolvedPackage, snippetGroups: [SnippetGroup], swiftTool: SwiftTool) { + init(package: ResolvedPackage, snippetGroups: [SnippetGroup], swiftCommandState: SwiftCommandState) { self.package = package self.snippetGroups = snippetGroups - self.swiftTool = swiftTool + self.swiftCommandState = swiftCommandState } var inputPrompt: String? { @@ -122,9 +122,9 @@ struct TopCard: Card { } if let index = Int(line), snippetGroups.indices.contains(index) { - return .push(SnippetGroupCard(snippetGroup: snippetGroups[index], swiftTool: swiftTool)) + return .push(SnippetGroupCard(snippetGroup: snippetGroups[index], swiftCommandState: swiftCommandState)) } else if let groupByName = snippetGroups.first(where: { $0.name == line }) { - return .push(SnippetGroupCard(snippetGroup: groupByName, swiftTool: swiftTool)) + return .push(SnippetGroupCard(snippetGroup: groupByName, swiftCommandState: swiftCommandState)) } else { print(red { "There is not a group by that name or index." }) return nil diff --git a/Sources/Commands/SwiftBuildTool.swift b/Sources/Commands/SwiftBuildCommand.swift similarity index 86% rename from Sources/Commands/SwiftBuildTool.swift rename to Sources/Commands/SwiftBuildCommand.swift index ed01e55084b..13c2d658621 100644 --- a/Sources/Commands/SwiftBuildTool.swift +++ b/Sources/Commands/SwiftBuildCommand.swift @@ -43,7 +43,7 @@ extension BuildSubset { } } -struct BuildToolOptions: ParsableArguments { +struct BuildCommandOptions: ParsableArguments { /// Returns the build subset specified with the options. func buildSubset(observabilityScope: ObservabilityScope) -> BuildSubset? { var allSubsets: [BuildSubset] = [] @@ -94,8 +94,8 @@ struct BuildToolOptions: ParsableArguments { public var shouldLinkStaticSwiftStdlib: Bool = false } -/// swift-build tool namespace -public struct SwiftBuildTool: SwiftCommand { +/// swift-build command namespace +public struct SwiftBuildCommand: SwiftCommand { public static var configuration = CommandConfiguration( commandName: "build", _superCommandName: "swift", @@ -108,16 +108,16 @@ public struct SwiftBuildTool: SwiftCommand { public var globalOptions: GlobalOptions @OptionGroup() - var options: BuildToolOptions + var options: BuildCommandOptions - public func run(_ swiftTool: SwiftTool) throws { + public func run(_ swiftCommandState: SwiftCommandState) throws { if options.shouldPrintBinPath { - return try print(swiftTool.productsBuildParameters.buildPath.description) + return try print(swiftCommandState.productsBuildParameters.buildPath.description) } if options.printManifestGraphviz { // FIXME: Doesn't seem ideal that we need an explicit build operation, but this concretely uses the `LLBuildManifest`. - guard let buildOperation = try swiftTool.createBuildSystem(explicitBuildSystem: .native) as? BuildOperation else { + guard let buildOperation = try swiftCommandState.createBuildSystem(explicitBuildSystem: .native) as? BuildOperation else { throw StringError("asked for native build system but did not get it") } let buildManifest = try buildOperation.getBuildManifest() @@ -129,10 +129,10 @@ public struct SwiftBuildTool: SwiftCommand { return } - guard let subset = options.buildSubset(observabilityScope: swiftTool.observabilityScope) else { + guard let subset = options.buildSubset(observabilityScope: swiftCommandState.observabilityScope) else { throw ExitCode.failure } - let buildSystem = try swiftTool.createBuildSystem( + let buildSystem = try swiftCommandState.createBuildSystem( explicitProduct: options.product, shouldLinkStaticSwiftStdlib: options.shouldLinkStaticSwiftStdlib, // command result output goes on stdout @@ -150,7 +150,7 @@ public struct SwiftBuildTool: SwiftCommand { } public extension _SwiftCommand { - func buildSystemProvider(_ swiftTool: SwiftTool) throws -> BuildSystemProvider { - swiftTool.defaultBuildSystemProvider + func buildSystemProvider(_ swiftCommandState: SwiftCommandState) throws -> BuildSystemProvider { + swiftCommandState.defaultBuildSystemProvider } } diff --git a/Sources/Commands/SwiftRunTool.swift b/Sources/Commands/SwiftRunCommand.swift similarity index 81% rename from Sources/Commands/SwiftRunTool.swift rename to Sources/Commands/SwiftRunCommand.swift index d9a9c91a4c3..bbf480ab23e 100644 --- a/Sources/Commands/SwiftRunTool.swift +++ b/Sources/Commands/SwiftRunCommand.swift @@ -48,13 +48,13 @@ extension RunError: CustomStringConvertible { } } -struct RunToolOptions: ParsableArguments { +struct RunCommandOptions: ParsableArguments { enum RunMode: EnumerableFlag { case repl case debugger case run - static func help(for value: RunToolOptions.RunMode) -> ArgumentHelp? { + static func help(for value: RunCommandOptions.RunMode) -> ArgumentHelp? { switch value { case .repl: return "Launch Swift REPL for the package" @@ -89,8 +89,8 @@ struct RunToolOptions: ParsableArguments { var arguments: [String] = [] } -/// swift-run tool namespace -public struct SwiftRunTool: SwiftCommand { +/// swift-run command namespace +public struct SwiftRunCommand: SwiftCommand { public static var configuration = CommandConfiguration( commandName: "run", _superCommandName: "swift", @@ -103,15 +103,15 @@ public struct SwiftRunTool: SwiftCommand { public var globalOptions: GlobalOptions @OptionGroup() - var options: RunToolOptions + var options: RunCommandOptions public var toolWorkspaceConfiguration: ToolWorkspaceConfiguration { return .init(wantsREPLProduct: options.mode == .repl) } - public func run(_ swiftTool: SwiftTool) throws { + public func run(_ swiftCommandState: SwiftCommandState) throws { if options.shouldBuildTests && options.shouldSkipBuild { - swiftTool.observabilityScope.emit( + swiftCommandState.observabilityScope.emit( .mutuallyExclusiveArgumentsError(arguments: ["--build-tests", "--skip-build"]) ) throw ExitCode.failure @@ -121,14 +121,14 @@ public struct SwiftRunTool: SwiftCommand { case .repl: // Load a custom package graph which has a special product for REPL. let graphLoader = { - try swiftTool.loadPackageGraph( + try swiftCommandState.loadPackageGraph( explicitProduct: self.options.executable ) } // Construct the build operation. // FIXME: We need to implement the build tool invocation closure here so that build tool plugins work with the REPL. rdar://86112934 - let buildSystem = try swiftTool.createBuildSystem( + let buildSystem = try swiftCommandState.createBuildSystem( explicitBuildSystem: .native, cacheBuildManifest: false, packageGraphLoader: graphLoader @@ -141,15 +141,15 @@ public struct SwiftRunTool: SwiftCommand { let arguments = try buildSystem.buildPlan.createREPLArguments() print("Launching Swift REPL with arguments: \(arguments.joined(separator: " "))") try self.run( - fileSystem: swiftTool.fileSystem, - executablePath: swiftTool.getTargetToolchain().swiftInterpreterPath, - originalWorkingDirectory: swiftTool.originalWorkingDirectory, + fileSystem: swiftCommandState.fileSystem, + executablePath: swiftCommandState.getTargetToolchain().swiftInterpreterPath, + originalWorkingDirectory: swiftCommandState.originalWorkingDirectory, arguments: arguments ) case .debugger: do { - let buildSystem = try swiftTool.createBuildSystem(explicitProduct: options.executable) + let buildSystem = try swiftCommandState.createBuildSystem(explicitProduct: options.executable) let productName = try findProductName(in: buildSystem.getPackageGraph()) if options.shouldBuildTests { try buildSystem.build(subset: .allIncludingTests) @@ -157,41 +157,41 @@ public struct SwiftRunTool: SwiftCommand { try buildSystem.build(subset: .product(productName)) } - let executablePath = try swiftTool.productsBuildParameters.buildPath.appending(component: productName) + let executablePath = try swiftCommandState.productsBuildParameters.buildPath.appending(component: productName) // Make sure we are running from the original working directory. - let cwd: AbsolutePath? = swiftTool.fileSystem.currentWorkingDirectory - if cwd == nil || swiftTool.originalWorkingDirectory != cwd { - try ProcessEnv.chdir(swiftTool.originalWorkingDirectory) + let cwd: AbsolutePath? = swiftCommandState.fileSystem.currentWorkingDirectory + if cwd == nil || swiftCommandState.originalWorkingDirectory != cwd { + try ProcessEnv.chdir(swiftCommandState.originalWorkingDirectory) } - let pathRelativeToWorkingDirectory = executablePath.relative(to: swiftTool.originalWorkingDirectory) - let lldbPath = try swiftTool.getTargetToolchain().getLLDB() + let pathRelativeToWorkingDirectory = executablePath.relative(to: swiftCommandState.originalWorkingDirectory) + let lldbPath = try swiftCommandState.getTargetToolchain().getLLDB() try exec(path: lldbPath.pathString, args: ["--", pathRelativeToWorkingDirectory.pathString] + options.arguments) } catch let error as RunError { - swiftTool.observabilityScope.emit(error) + swiftCommandState.observabilityScope.emit(error) throw ExitCode.failure } case .run: // Detect deprecated uses of swift run to interpret scripts. - if let executable = options.executable, try isValidSwiftFilePath(fileSystem: swiftTool.fileSystem, path: executable) { - swiftTool.observabilityScope.emit(.runFileDeprecation) + if let executable = options.executable, try isValidSwiftFilePath(fileSystem: swiftCommandState.fileSystem, path: executable) { + swiftCommandState.observabilityScope.emit(.runFileDeprecation) // Redirect execution to the toolchain's swift executable. - let swiftInterpreterPath = try swiftTool.getTargetToolchain().swiftInterpreterPath + let swiftInterpreterPath = try swiftCommandState.getTargetToolchain().swiftInterpreterPath // Prepend the script to interpret to the arguments. let arguments = [executable] + options.arguments try self.run( - fileSystem: swiftTool.fileSystem, + fileSystem: swiftCommandState.fileSystem, executablePath: swiftInterpreterPath, - originalWorkingDirectory: swiftTool.originalWorkingDirectory, + originalWorkingDirectory: swiftCommandState.originalWorkingDirectory, arguments: arguments ) return } do { - let buildSystem = try swiftTool.createBuildSystem(explicitProduct: options.executable) + let buildSystem = try swiftCommandState.createBuildSystem(explicitProduct: options.executable) let productName = try findProductName(in: buildSystem.getPackageGraph()) if options.shouldBuildTests { try buildSystem.build(subset: .allIncludingTests) @@ -199,17 +199,17 @@ public struct SwiftRunTool: SwiftCommand { try buildSystem.build(subset: .product(productName)) } - let executablePath = try swiftTool.productsBuildParameters.buildPath.appending(component: productName) + let executablePath = try swiftCommandState.productsBuildParameters.buildPath.appending(component: productName) try self.run( - fileSystem: swiftTool.fileSystem, + fileSystem: swiftCommandState.fileSystem, executablePath: executablePath, - originalWorkingDirectory: swiftTool.originalWorkingDirectory, + originalWorkingDirectory: swiftCommandState.originalWorkingDirectory, arguments: options.arguments ) } catch Diagnostics.fatalError { throw ExitCode.failure } catch let error as RunError { - swiftTool.observabilityScope.emit(error) + swiftCommandState.observabilityScope.emit(error) throw ExitCode.failure } } diff --git a/Sources/Commands/SwiftTestTool.swift b/Sources/Commands/SwiftTestCommand.swift similarity index 82% rename from Sources/Commands/SwiftTestTool.swift rename to Sources/Commands/SwiftTestCommand.swift index 1a713702f43..45bdca6fd70 100644 --- a/Sources/Commands/SwiftTestTool.swift +++ b/Sources/Commands/SwiftTestCommand.swift @@ -82,19 +82,19 @@ struct SharedOptions: ParsableArguments { var _enableSwiftTestingLibrarySupport: Bool? /// Whether to enable support for swift-testing. - func enableSwiftTestingLibrarySupport(swiftTool: SwiftTool) throws -> Bool { + func enableSwiftTestingLibrarySupport(swiftCommandState: SwiftCommandState) throws -> Bool { // Honor the user's explicit command-line selection, if any. if let callerSuppliedValue = _enableSwiftTestingLibrarySupport { return callerSuppliedValue } // If the active package has a dependency on swift-testing, automatically enable support for it so that extra steps are not needed. - let workspace = try swiftTool.getActiveWorkspace() - let root = try swiftTool.getWorkspaceRoot() + let workspace = try swiftCommandState.getActiveWorkspace() + let root = try swiftCommandState.getWorkspaceRoot() let rootManifests = try temp_await { workspace.loadRootManifests( packages: root.packages, - observabilityScope: swiftTool.observabilityScope, + observabilityScope: swiftCommandState.observabilityScope, completion: $0 ) } @@ -107,7 +107,7 @@ struct SharedOptions: ParsableArguments { .map(String.init(describing:)) .contains("swift-testing") if isEnabledByDependency { - swiftTool.observabilityScope.emit(debug: "Enabling swift-testing support due to its presence as a package dependency.") + swiftCommandState.observabilityScope.emit(debug: "Enabling swift-testing support due to its presence as a package dependency.") return true } @@ -118,7 +118,7 @@ struct SharedOptions: ParsableArguments { .map(String.init(describing:)) .contains("swift-testing") if isEnabledByName { - swiftTool.observabilityScope.emit(debug: "Enabling swift-testing support because it is a root package.") + swiftCommandState.observabilityScope.emit(debug: "Enabling swift-testing support because it is a root package.") return true } @@ -127,7 +127,7 @@ struct SharedOptions: ParsableArguments { } } -struct TestToolOptions: ParsableArguments { +struct TestCommandOptions: ParsableArguments { @OptionGroup() var globalOptions: GlobalOptions @@ -228,7 +228,7 @@ public enum TestOutput: String, ExpressibleByArgument { } /// swift-test tool namespace -public struct SwiftTestTool: SwiftCommand { +public struct SwiftTestCommand: SwiftCommand { public static var configuration = CommandConfiguration( commandName: "test", _superCommandName: "swift", @@ -245,33 +245,39 @@ public struct SwiftTestTool: SwiftCommand { } @OptionGroup() - var options: TestToolOptions + var options: TestCommandOptions // MARK: - XCTest - private func xctestRun(_ swiftTool: SwiftTool) throws { + private func xctestRun(_ swiftCommandState: SwiftCommandState) throws { // validate XCTest available on darwin based systems - let toolchain = try swiftTool.getTargetToolchain() - let isHostTestingAvailable = try swiftTool.getHostToolchain().swiftSDK.supportsTesting + let toolchain = try swiftCommandState.getTargetToolchain() + let isHostTestingAvailable = try swiftCommandState.getHostToolchain().swiftSDK.supportsTesting if (toolchain.targetTriple.isDarwin() && toolchain.xctestPath == nil) || !isHostTestingAvailable { throw TestError.xctestNotAvailable } - let buildParameters = try swiftTool.buildParametersForTest(options: self.options, library: .xctest) + let buildParameters = try swiftCommandState.buildParametersForTest(options: self.options, library: .xctest) // Remove test output from prior runs and validate priors. if self.options.enableExperimentalTestOutput && buildParameters.triple.supportsTestSummary { _ = try? localFileSystem.removeFileTree(buildParameters.testOutputPath) } - let testProducts = try buildTestsIfNeeded(swiftTool: swiftTool, library: .xctest) + let testProducts = try buildTestsIfNeeded(swiftCommandState: swiftCommandState, library: .xctest) if !self.options.shouldRunInParallel { - let xctestArgs = try xctestArgs(for: testProducts, swiftTool: swiftTool) - try runTestProducts(testProducts, additionalArguments: xctestArgs, buildParameters: buildParameters, swiftTool: swiftTool, library: .xctest) + let xctestArgs = try xctestArgs(for: testProducts, swiftCommandState: swiftCommandState) + try runTestProducts( + testProducts, + additionalArguments: xctestArgs, + buildParameters: buildParameters, + swiftCommandState: swiftCommandState, + library: .xctest + ) } else { let testSuites = try TestingSupport.getTestSuites( in: testProducts, - swiftTool: swiftTool, + swiftCommandState: swiftCommandState, enableCodeCoverage: options.enableCodeCoverage, shouldSkipBuilding: options.sharedOptions.shouldSkipBuilding, experimentalTestOutput: options.enableExperimentalTestOutput, @@ -279,44 +285,44 @@ public struct SwiftTestTool: SwiftCommand { ) let tests = try testSuites .filteredTests(specifier: options.testCaseSpecifier) - .skippedTests(specifier: options.skippedTests(fileSystem: swiftTool.fileSystem)) + .skippedTests(specifier: options.skippedTests(fileSystem: swiftCommandState.fileSystem)) // If there were no matches, emit a warning and exit. if tests.isEmpty { - swiftTool.observabilityScope.emit(.noMatchingTests) - try generateXUnitOutputIfRequested(for: [], swiftTool: swiftTool) + swiftCommandState.observabilityScope.emit(.noMatchingTests) + try generateXUnitOutputIfRequested(for: [], swiftCommandState: swiftCommandState) return } // Clean out the code coverage directory that may contain stale // profraw files from a previous run of the code coverage tool. if self.options.enableCodeCoverage { - try swiftTool.fileSystem.removeFileTree(buildParameters.codeCovPath) + try swiftCommandState.fileSystem.removeFileTree(buildParameters.codeCovPath) } // Run the tests using the parallel runner. let runner = ParallelTestRunner( bundlePaths: testProducts.map { $0.bundlePath }, - cancellator: swiftTool.cancellator, + cancellator: swiftCommandState.cancellator, toolchain: toolchain, numJobs: options.numberOfWorkers ?? ProcessInfo.processInfo.activeProcessorCount, buildOptions: globalOptions.build, buildParameters: buildParameters, - shouldOutputSuccess: swiftTool.logLevel <= .info, - observabilityScope: swiftTool.observabilityScope + shouldOutputSuccess: swiftCommandState.logLevel <= .info, + observabilityScope: swiftCommandState.observabilityScope ) let testResults = try runner.run(tests) - try generateXUnitOutputIfRequested(for: testResults, swiftTool: swiftTool) + try generateXUnitOutputIfRequested(for: testResults, swiftCommandState: swiftCommandState) // process code Coverage if request if self.options.enableCodeCoverage, runner.ranSuccessfully { - try processCodeCoverage(testProducts, swiftTool: swiftTool, library: .xctest) + try processCodeCoverage(testProducts, swiftCommandState: swiftCommandState, library: .xctest) } if !runner.ranSuccessfully { - swiftTool.executionStatus = .failure + swiftCommandState.executionStatus = .failure } if self.options.enableExperimentalTestOutput, !runner.ranSuccessfully { @@ -325,10 +331,10 @@ public struct SwiftTestTool: SwiftCommand { } } - private func xctestArgs(for testProducts: [BuiltTestProduct], swiftTool: SwiftTool) throws -> [String] { + private func xctestArgs(for testProducts: [BuiltTestProduct], swiftCommandState: SwiftCommandState) throws -> [String] { switch options.testCaseSpecifier { case .none: - if case .skip = options.skippedTests(fileSystem: swiftTool.fileSystem) { + if case .skip = options.skippedTests(fileSystem: swiftCommandState.fileSystem) { fallthrough } else { return [] @@ -337,13 +343,13 @@ public struct SwiftTestTool: SwiftCommand { case .regex, .specific, .skip: // If old specifier `-s` option was used, emit deprecation notice. if case .specific = options.testCaseSpecifier { - swiftTool.observabilityScope.emit(warning: "'--specifier' option is deprecated; use '--filter' instead") + swiftCommandState.observabilityScope.emit(warning: "'--specifier' option is deprecated; use '--filter' instead") } // Find the tests we need to run. let testSuites = try TestingSupport.getTestSuites( in: testProducts, - swiftTool: swiftTool, + swiftCommandState: swiftCommandState, enableCodeCoverage: options.enableCodeCoverage, shouldSkipBuilding: options.sharedOptions.shouldSkipBuilding, experimentalTestOutput: options.enableExperimentalTestOutput, @@ -351,11 +357,11 @@ public struct SwiftTestTool: SwiftCommand { ) let tests = try testSuites .filteredTests(specifier: options.testCaseSpecifier) - .skippedTests(specifier: options.skippedTests(fileSystem: swiftTool.fileSystem)) + .skippedTests(specifier: options.skippedTests(fileSystem: swiftCommandState.fileSystem)) // If there were no matches, emit a warning. if tests.isEmpty { - swiftTool.observabilityScope.emit(.noMatchingTests) + swiftCommandState.observabilityScope.emit(.noMatchingTests) } return TestRunner.xctestArguments(forTestSpecifiers: tests.map(\.specifier)) @@ -363,13 +369,16 @@ public struct SwiftTestTool: SwiftCommand { } /// Generate xUnit file if requested. - private func generateXUnitOutputIfRequested(for testResults: [ParallelTestRunner.TestResult], swiftTool: SwiftTool) throws { + private func generateXUnitOutputIfRequested( + for testResults: [ParallelTestRunner.TestResult], + swiftCommandState: SwiftCommandState + ) throws { guard let xUnitOutput = options.xUnitOutput else { return } let generator = XUnitGenerator( - fileSystem: swiftTool.fileSystem, + fileSystem: swiftCommandState.fileSystem, results: testResults ) try generator.generate(at: xUnitOutput) @@ -377,48 +386,60 @@ public struct SwiftTestTool: SwiftCommand { // MARK: - swift-testing - private func swiftTestingRun(_ swiftTool: SwiftTool) throws { - let buildParameters = try swiftTool.buildParametersForTest(options: self.options, library: .swiftTesting) - let testProducts = try buildTestsIfNeeded(swiftTool: swiftTool, library: .swiftTesting) + private func swiftTestingRun(_ swiftCommandState: SwiftCommandState) throws { + let buildParameters = try swiftCommandState.buildParametersForTest(options: self.options, library: .swiftTesting) + let testProducts = try buildTestsIfNeeded(swiftCommandState: swiftCommandState, library: .swiftTesting) let additionalArguments = Array(CommandLine.arguments.dropFirst()) - try runTestProducts(testProducts, additionalArguments: additionalArguments, buildParameters: buildParameters, swiftTool: swiftTool, library: .swiftTesting) + try runTestProducts( + testProducts, + additionalArguments: additionalArguments, + buildParameters: buildParameters, + swiftCommandState: swiftCommandState, + library: .swiftTesting + ) } // MARK: - Common implementation - public func run(_ swiftTool: SwiftTool) throws { + public func run(_ swiftCommandState: SwiftCommandState) throws { do { // Validate commands arguments - try self.validateArguments(observabilityScope: swiftTool.observabilityScope) + try self.validateArguments(observabilityScope: swiftCommandState.observabilityScope) } catch { - swiftTool.observabilityScope.emit(error) + swiftCommandState.observabilityScope.emit(error) throw ExitCode.failure } if self.options.shouldPrintCodeCovPath { - try printCodeCovPath(swiftTool) + try printCodeCovPath(swiftCommandState) } else if self.options._deprecated_shouldListTests { // backward compatibility 6/2022 for deprecation of flag into a subcommand let command = try List.parse() - try command.run(swiftTool) + try command.run(swiftCommandState) } else { - if try options.sharedOptions.enableSwiftTestingLibrarySupport(swiftTool: swiftTool) { - try swiftTestingRun(swiftTool) + if try options.sharedOptions.enableSwiftTestingLibrarySupport(swiftCommandState: swiftCommandState) { + try swiftTestingRun(swiftCommandState) } if options.sharedOptions.enableXCTestSupport { - try xctestRun(swiftTool) + try xctestRun(swiftCommandState) } } } - private func runTestProducts(_ testProducts: [BuiltTestProduct], additionalArguments: [String], buildParameters: BuildParameters, swiftTool: SwiftTool, library: BuildParameters.Testing.Library) throws { + private func runTestProducts( + _ testProducts: [BuiltTestProduct], + additionalArguments: [String], + buildParameters: BuildParameters, + swiftCommandState: SwiftCommandState, + library: BuildParameters.Testing.Library + ) throws { // Clean out the code coverage directory that may contain stale // profraw files from a previous run of the code coverage tool. if self.options.enableCodeCoverage { - try swiftTool.fileSystem.removeFileTree(buildParameters.codeCovPath) + try swiftCommandState.fileSystem.removeFileTree(buildParameters.codeCovPath) } - let toolchain = try swiftTool.getTargetToolchain() + let toolchain = try swiftCommandState.getTargetToolchain() let testEnv = try TestingSupport.constructTestEnvironment( toolchain: toolchain, buildParameters: buildParameters, @@ -428,10 +449,10 @@ public struct SwiftTestTool: SwiftCommand { let runner = TestRunner( bundlePaths: testProducts.map { library == .xctest ? $0.bundlePath : $0.binaryPath }, additionalArguments: additionalArguments, - cancellator: swiftTool.cancellator, + cancellator: swiftCommandState.cancellator, toolchain: toolchain, testEnv: testEnv, - observabilityScope: swiftTool.observabilityScope, + observabilityScope: swiftCommandState.observabilityScope, library: library ) @@ -442,11 +463,11 @@ public struct SwiftTestTool: SwiftCommand { print($0, terminator: "") }) if !ranSuccessfully { - swiftTool.executionStatus = .failure + swiftCommandState.executionStatus = .failure } if self.options.enableCodeCoverage, ranSuccessfully { - try processCodeCoverage(testProducts, swiftTool: swiftTool, library: library) + try processCodeCoverage(testProducts, swiftCommandState: swiftCommandState, library: library) } if self.options.enableExperimentalTestOutput, !ranSuccessfully { @@ -486,13 +507,17 @@ public struct SwiftTestTool: SwiftCommand { } /// Processes the code coverage data and emits a json. - private func processCodeCoverage(_ testProducts: [BuiltTestProduct], swiftTool: SwiftTool, library: BuildParameters.Testing.Library) throws { - let workspace = try swiftTool.getActiveWorkspace() - let root = try swiftTool.getWorkspaceRoot() + private func processCodeCoverage( + _ testProducts: [BuiltTestProduct], + swiftCommandState: SwiftCommandState, + library: BuildParameters.Testing.Library + ) throws { + let workspace = try swiftCommandState.getActiveWorkspace() + let root = try swiftCommandState.getWorkspaceRoot() let rootManifests = try temp_await { workspace.loadRootManifests( packages: root.packages, - observabilityScope: swiftTool.observabilityScope, + observabilityScope: swiftCommandState.observabilityScope, completion: $0 ) } @@ -501,24 +526,24 @@ public struct SwiftTestTool: SwiftCommand { } // Merge all the profraw files to produce a single profdata file. - try mergeCodeCovRawDataFiles(swiftTool: swiftTool, library: library) + try mergeCodeCovRawDataFiles(swiftCommandState: swiftCommandState, library: library) - let buildParameters = try swiftTool.buildParametersForTest(options: self.options, library: library) + let buildParameters = try swiftCommandState.buildParametersForTest(options: self.options, library: library) for product in testProducts { // Export the codecov data as JSON. let jsonPath = buildParameters.codeCovAsJSONPath(packageName: rootManifest.displayName) - try exportCodeCovAsJSON(to: jsonPath, testBinary: product.binaryPath, swiftTool: swiftTool, library: library) + try exportCodeCovAsJSON(to: jsonPath, testBinary: product.binaryPath, swiftCommandState: swiftCommandState, library: library) } } /// Merges all profraw profiles in codecoverage directory into default.profdata file. - private func mergeCodeCovRawDataFiles(swiftTool: SwiftTool, library: BuildParameters.Testing.Library) throws { + private func mergeCodeCovRawDataFiles(swiftCommandState: SwiftCommandState, library: BuildParameters.Testing.Library) throws { // Get the llvm-prof tool. - let llvmProf = try swiftTool.getTargetToolchain().getLLVMProf() + let llvmProf = try swiftCommandState.getTargetToolchain().getLLVMProf() // Get the profraw files. - let buildParameters = try swiftTool.buildParametersForTest(options: self.options, library: library) - let codeCovFiles = try swiftTool.fileSystem.getDirectoryContents(buildParameters.codeCovPath) + let buildParameters = try swiftCommandState.buildParametersForTest(options: self.options, library: library) + let codeCovFiles = try swiftCommandState.fileSystem.getDirectoryContents(buildParameters.codeCovPath) // Construct arguments for invoking the llvm-prof tool. var args = [llvmProf.pathString, "merge", "-sparse"] @@ -534,10 +559,15 @@ public struct SwiftTestTool: SwiftCommand { } /// Exports profdata as a JSON file. - private func exportCodeCovAsJSON(to path: AbsolutePath, testBinary: AbsolutePath, swiftTool: SwiftTool, library: BuildParameters.Testing.Library) throws { + private func exportCodeCovAsJSON( + to path: AbsolutePath, + testBinary: AbsolutePath, + swiftCommandState: SwiftCommandState, + library: BuildParameters.Testing.Library + ) throws { // Export using the llvm-cov tool. - let llvmCov = try swiftTool.getTargetToolchain().getLLVMCov() - let buildParameters = try swiftTool.buildParametersForTest(options: self.options, library: library) + let llvmCov = try swiftCommandState.getTargetToolchain().getLLVMCov() + let buildParameters = try swiftCommandState.buildParametersForTest(options: self.options, library: library) let args = [ llvmCov.pathString, "export", @@ -550,15 +580,22 @@ public struct SwiftTestTool: SwiftCommand { let output = try result.utf8Output() + result.utf8stderrOutput() throw StringError("Unable to export code coverage:\n \(output)") } - try swiftTool.fileSystem.writeFileContents(path, bytes: ByteString(result.output.get())) + try swiftCommandState.fileSystem.writeFileContents(path, bytes: ByteString(result.output.get())) } /// Builds the "test" target if enabled in options. /// /// - Returns: The paths to the build test products. - private func buildTestsIfNeeded(swiftTool: SwiftTool, library: BuildParameters.Testing.Library) throws -> [BuiltTestProduct] { - let buildParameters = try swiftTool.buildParametersForTest(options: self.options, library: library) - return try Commands.buildTestsIfNeeded(swiftTool: swiftTool, buildParameters: buildParameters, testProduct: self.options.sharedOptions.testProduct) + private func buildTestsIfNeeded( + swiftCommandState: SwiftCommandState, + library: BuildParameters.Testing.Library + ) throws -> [BuiltTestProduct] { + let buildParameters = try swiftCommandState.buildParametersForTest(options: self.options, library: library) + return try Commands.buildTestsIfNeeded( + swiftCommandState: swiftCommandState, + buildParameters: buildParameters, + testProduct: self.options.sharedOptions.testProduct + ) } /// Private function that validates the commands arguments @@ -592,33 +629,33 @@ public struct SwiftTestTool: SwiftCommand { public init() {} } -extension SwiftTestTool { - func printCodeCovPath(_ swiftTool: SwiftTool) throws { - let workspace = try swiftTool.getActiveWorkspace() - let root = try swiftTool.getWorkspaceRoot() +extension SwiftTestCommand { + func printCodeCovPath(_ swiftCommandState: SwiftCommandState) throws { + let workspace = try swiftCommandState.getActiveWorkspace() + let root = try swiftCommandState.getWorkspaceRoot() let rootManifests = try temp_await { workspace.loadRootManifests( packages: root.packages, - observabilityScope: swiftTool.observabilityScope, + observabilityScope: swiftCommandState.observabilityScope, completion: $0 ) } guard let rootManifest = rootManifests.values.first else { throw StringError("invalid manifests at \(root.packages)") } - let buildParameters = try swiftTool.buildParametersForTest(enableCodeCoverage: true, library: .xctest) + let buildParameters = try swiftCommandState.buildParametersForTest(enableCodeCoverage: true, library: .xctest) print(buildParameters.codeCovAsJSONPath(packageName: rootManifest.displayName)) } } -extension SwiftTestTool { +extension SwiftTestCommand { struct Last: SwiftCommand { @OptionGroup(visibility: .hidden) var globalOptions: GlobalOptions - func run(_ swiftTool: SwiftTool) throws { - try SwiftTestTool.handleTestOutput( - buildParameters: try swiftTool.productsBuildParameters, + func run(_ swiftCommandState: SwiftCommandState) throws { + try SwiftTestCommand.handleTestOutput( + buildParameters: try swiftCommandState.productsBuildParameters, packagePath: localFileSystem.currentWorkingDirectory ?? .root // by definition runs in the current working directory ) } @@ -641,12 +678,16 @@ extension SwiftTestTool { // MARK: - XCTest - private func xctestRun(_ swiftTool: SwiftTool) throws { - let buildParameters = try swiftTool.buildParametersForTest(enableCodeCoverage: false, shouldSkipBuilding: sharedOptions.shouldSkipBuilding, library: .xctest) - let testProducts = try buildTestsIfNeeded(swiftTool: swiftTool, buildParameters: buildParameters) + private func xctestRun(_ swiftCommandState: SwiftCommandState) throws { + let buildParameters = try swiftCommandState.buildParametersForTest( + enableCodeCoverage: false, + shouldSkipBuilding: sharedOptions.shouldSkipBuilding, + library: .xctest + ) + let testProducts = try buildTestsIfNeeded(swiftCommandState: swiftCommandState, buildParameters: buildParameters) let testSuites = try TestingSupport.getTestSuites( in: testProducts, - swiftTool: swiftTool, + swiftCommandState: swiftCommandState, enableCodeCoverage: false, shouldSkipBuilding: sharedOptions.shouldSkipBuilding, experimentalTestOutput: false, @@ -661,11 +702,18 @@ extension SwiftTestTool { // MARK: - swift-testing - private func swiftTestingRun(_ swiftTool: SwiftTool) throws { - let buildParameters = try swiftTool.buildParametersForTest(enableCodeCoverage: false, shouldSkipBuilding: sharedOptions.shouldSkipBuilding, library: .swiftTesting) - let testProducts = try buildTestsIfNeeded(swiftTool: swiftTool, buildParameters: buildParameters) + private func swiftTestingRun(_ swiftCommandState: SwiftCommandState) throws { + let buildParameters = try swiftCommandState.buildParametersForTest( + enableCodeCoverage: false, + shouldSkipBuilding: sharedOptions.shouldSkipBuilding, + library: .swiftTesting + ) + let testProducts = try buildTestsIfNeeded( + swiftCommandState: swiftCommandState, + buildParameters: buildParameters + ) - let toolchain = try swiftTool.getTargetToolchain() + let toolchain = try swiftCommandState.getTargetToolchain() let testEnv = try TestingSupport.constructTestEnvironment( toolchain: toolchain, buildParameters: buildParameters, @@ -675,10 +723,10 @@ extension SwiftTestTool { let runner = TestRunner( bundlePaths: testProducts.map(\.binaryPath), additionalArguments: ["--list-tests"], - cancellator: swiftTool.cancellator, + cancellator: swiftCommandState.cancellator, toolchain: toolchain, testEnv: testEnv, - observabilityScope: swiftTool.observabilityScope, + observabilityScope: swiftCommandState.observabilityScope, library: .swiftTesting ) @@ -689,23 +737,30 @@ extension SwiftTestTool { print($0, terminator: "") }) if !ranSuccessfully { - swiftTool.executionStatus = .failure + swiftCommandState.executionStatus = .failure } } // MARK: - Common implementation - func run(_ swiftTool: SwiftTool) throws { - if try sharedOptions.enableSwiftTestingLibrarySupport(swiftTool: swiftTool) { - try swiftTestingRun(swiftTool) + func run(_ swiftCommandState: SwiftCommandState) throws { + if try sharedOptions.enableSwiftTestingLibrarySupport(swiftCommandState: swiftCommandState) { + try swiftTestingRun(swiftCommandState) } if sharedOptions.enableXCTestSupport { - try xctestRun(swiftTool) + try xctestRun(swiftCommandState) } } - private func buildTestsIfNeeded(swiftTool: SwiftTool, buildParameters: BuildParameters) throws -> [BuiltTestProduct] { - return try Commands.buildTestsIfNeeded(swiftTool: swiftTool, buildParameters: buildParameters, testProduct: self.sharedOptions.testProduct) + private func buildTestsIfNeeded( + swiftCommandState: SwiftCommandState, + buildParameters: BuildParameters + ) throws -> [BuiltTestProduct] { + return try Commands.buildTestsIfNeeded( + swiftCommandState: swiftCommandState, + buildParameters: buildParameters, + testProduct: self.sharedOptions.testProduct + ) } } } @@ -1252,9 +1307,9 @@ final class XUnitGenerator { } } -extension SwiftTool { +extension SwiftCommandState { func buildParametersForTest( - options: TestToolOptions, + options: TestCommandOptions, library: BuildParameters.Testing.Library ) throws -> BuildParameters { var result = try self.buildParametersForTest( @@ -1264,14 +1319,14 @@ extension SwiftTool { experimentalTestOutput: options.enableExperimentalTestOutput, library: library ) - if try options.sharedOptions.enableSwiftTestingLibrarySupport(swiftTool: self) { + if try options.sharedOptions.enableSwiftTestingLibrarySupport(swiftCommandState: self) { result.flags.swiftCompilerFlags += ["-DSWIFT_PM_SUPPORTS_SWIFT_TESTING"] } return result } } -extension TestToolOptions { +extension TestCommandOptions { func skippedTests(fileSystem: FileSystem) -> TestCaseSpecifier { // TODO: Remove this once the environment variable is no longer used. if let override = skippedTestsOverride(fileSystem: fileSystem) { @@ -1323,8 +1378,12 @@ private extension Basics.Diagnostic { /// Builds the "test" target if enabled in options. /// /// - Returns: The paths to the build test products. -private func buildTestsIfNeeded(swiftTool: SwiftTool, buildParameters: BuildParameters, testProduct: String?) throws -> [BuiltTestProduct] { - let buildSystem = try swiftTool.createBuildSystem(productsBuildParameters: buildParameters) +private func buildTestsIfNeeded( + swiftCommandState: SwiftCommandState, + buildParameters: BuildParameters, + testProduct: String? +) throws -> [BuiltTestProduct] { + let buildSystem = try swiftCommandState.createBuildSystem(productsBuildParameters: buildParameters) let subset = testProduct.map(BuildSubset.product) ?? .allIncludingTests try buildSystem.build(subset: subset) diff --git a/Sources/Commands/Utilities/APIDigester.swift b/Sources/Commands/Utilities/APIDigester.swift index cfc5438d48d..2826d9cd266 100644 --- a/Sources/Commands/Utilities/APIDigester.swift +++ b/Sources/Commands/Utilities/APIDigester.swift @@ -73,7 +73,7 @@ struct APIDigesterBaselineDumper { at baselineDir: AbsolutePath?, force: Bool, logLevel: Basics.Diagnostic.Severity, - swiftTool: SwiftTool + swiftCommandState: SwiftCommandState ) throws -> AbsolutePath { var modulesToDiff = modulesToDiff let apiDiffDir = productsBuildParameters.apiDiff @@ -85,7 +85,7 @@ struct APIDigesterBaselineDumper { if !force { // Baselines which already exist don't need to be regenerated. modulesToDiff = modulesToDiff.filter { - !swiftTool.fileSystem.exists(baselinePath($0)) + !swiftCommandState.fileSystem.exists(baselinePath($0)) } } @@ -96,8 +96,8 @@ struct APIDigesterBaselineDumper { // Setup a temporary directory where we can checkout and build the baseline treeish. let baselinePackageRoot = apiDiffDir.appending("\(baselineRevision.identifier)-checkout") - if swiftTool.fileSystem.exists(baselinePackageRoot) { - try swiftTool.fileSystem.removeFileTree(baselinePackageRoot) + if swiftCommandState.fileSystem.exists(baselinePackageRoot) { + try swiftCommandState.fileSystem.removeFileTree(baselinePackageRoot) } // Clone the current package in a sandbox and checkout the baseline revision. @@ -115,7 +115,7 @@ struct APIDigesterBaselineDumper { // Create the workspace for this package. let workspace = try Workspace( forRootPackage: baselinePackageRoot, - cancellator: swiftTool.cancellator + cancellator: swiftCommandState.cancellator ) let graph = try workspace.loadPackageGraph( @@ -137,7 +137,7 @@ struct APIDigesterBaselineDumper { // Build the baseline module. // FIXME: We need to implement the build tool invocation closure here so that build tool plugins work with the APIDigester. rdar://86112934 - let buildSystem = try swiftTool.createBuildSystem( + let buildSystem = try swiftCommandState.createBuildSystem( explicitBuildSystem: .native, cacheBuildManifest: false, productsBuildParameters: productsBuildParameters, @@ -147,7 +147,7 @@ struct APIDigesterBaselineDumper { try buildSystem.build() // Dump the SDK JSON. - try swiftTool.fileSystem.createDirectory(baselineDir, recursive: true) + try swiftCommandState.fileSystem.createDirectory(baselineDir, recursive: true) let group = DispatchGroup() let semaphore = DispatchSemaphore(value: Int(productsBuildParameters.workers)) let errors = ThreadSafeArrayStore() diff --git a/Sources/Commands/Utilities/PluginDelegate.swift b/Sources/Commands/Utilities/PluginDelegate.swift index d4ad1554444..72eb0c7d387 100644 --- a/Sources/Commands/Utilities/PluginDelegate.swift +++ b/Sources/Commands/Utilities/PluginDelegate.swift @@ -22,12 +22,12 @@ import class TSCBasic.Process import struct TSCBasic.ProcessResult final class PluginDelegate: PluginInvocationDelegate { - let swiftTool: SwiftTool + let swiftCommandState: SwiftCommandState let plugin: PluginTarget var lineBufferedOutput: Data - init(swiftTool: SwiftTool, plugin: PluginTarget) { - self.swiftTool = swiftTool + init(swiftCommandState: SwiftCommandState, plugin: PluginTarget) { + self.swiftCommandState = swiftCommandState self.plugin = plugin self.lineBufferedOutput = Data() } @@ -51,12 +51,12 @@ final class PluginDelegate: PluginInvocationDelegate { } func pluginEmittedDiagnostic(_ diagnostic: Basics.Diagnostic) { - swiftTool.observabilityScope.emit(diagnostic) + swiftCommandState.observabilityScope.emit(diagnostic) } func pluginEmittedProgress(_ message: String) { - swiftTool.outputStream.write("[\(plugin.name)] \(message)\n") - swiftTool.outputStream.flush() + swiftCommandState.outputStream.write("[\(plugin.name)] \(message)\n") + swiftCommandState.outputStream.flush() } func pluginRequestedBuildOperation( @@ -111,7 +111,7 @@ final class PluginDelegate: PluginInvocationDelegate { parameters: PluginInvocationBuildParameters ) throws -> PluginInvocationBuildResult { // Configure the build parameters. - var buildParameters = try self.swiftTool.productsBuildParameters + var buildParameters = try self.swiftCommandState.productsBuildParameters switch parameters.configuration { case .debug: buildParameters.configuration = .debug @@ -155,10 +155,10 @@ final class PluginDelegate: PluginInvocationDelegate { let bufferedOutputStream = BufferedOutputByteStream() let outputStream = TeeOutputByteStream([bufferedOutputStream]) if parameters.echoLogs { - outputStream.addStream(swiftTool.outputStream) + outputStream.addStream(swiftCommandState.outputStream) } - let buildSystem = try swiftTool.createBuildSystem( + let buildSystem = try swiftCommandState.createBuildSystem( explicitBuildSystem: .native, explicitProduct: explicitProduct, cacheBuildManifest: false, @@ -219,24 +219,24 @@ final class PluginDelegate: PluginInvocationDelegate { ) throws -> PluginInvocationTestResult { // Build the tests. Ideally we should only build those that match the subset, but we don't have a way to know // which ones they are until we've built them and can examine the binaries. - let toolchain = try swiftTool.getHostToolchain() - var toolsBuildParameters = try swiftTool.toolsBuildParameters + let toolchain = try swiftCommandState.getHostToolchain() + var toolsBuildParameters = try swiftCommandState.toolsBuildParameters toolsBuildParameters.testingParameters.enableTestability = true toolsBuildParameters.testingParameters.enableCodeCoverage = parameters.enableCodeCoverage - let buildSystem = try swiftTool.createBuildSystem(toolsBuildParameters: toolsBuildParameters) + let buildSystem = try swiftCommandState.createBuildSystem(toolsBuildParameters: toolsBuildParameters) try buildSystem.build(subset: .allIncludingTests) // Clean out the code coverage directory that may contain stale `profraw` files from a previous run of // the code coverage tool. if parameters.enableCodeCoverage { - try swiftTool.fileSystem.removeFileTree(toolsBuildParameters.codeCovPath) + try swiftCommandState.fileSystem.removeFileTree(toolsBuildParameters.codeCovPath) } // Construct the environment we'll pass down to the tests. let testEnvironment = try TestingSupport.constructTestEnvironment( toolchain: toolchain, buildParameters: toolsBuildParameters, - sanitizers: swiftTool.options.build.sanitizers + sanitizers: swiftCommandState.options.build.sanitizers ) // Iterate over the tests and run those that match the filter. @@ -246,11 +246,11 @@ final class PluginDelegate: PluginInvocationDelegate { // Get the test suites in the bundle. Each is just a container for test cases. let testSuites = try TestingSupport.getTestSuites( fromTestAt: testProduct.bundlePath, - swiftTool: swiftTool, + swiftCommandState: swiftCommandState, enableCodeCoverage: parameters.enableCodeCoverage, shouldSkipBuilding: false, experimentalTestOutput: false, - sanitizers: swiftTool.options.build.sanitizers + sanitizers: swiftCommandState.options.build.sanitizers ) for testSuite in testSuites { // Each test suite is just a container for test cases (confusingly called "tests", @@ -275,10 +275,10 @@ final class PluginDelegate: PluginInvocationDelegate { let testRunner = TestRunner( bundlePaths: [testProduct.bundlePath], additionalArguments: additionalArguments, - cancellator: swiftTool.cancellator, + cancellator: swiftCommandState.cancellator, toolchain: toolchain, testEnv: testEnvironment, - observabilityScope: swiftTool.observabilityScope, + observabilityScope: swiftCommandState.observabilityScope, library: .xctest) // FIXME: support both libraries // Run the test — for now we run the sequentially so we can capture accurate timing results. @@ -321,7 +321,7 @@ final class PluginDelegate: PluginInvocationDelegate { if parameters.enableCodeCoverage { // Use `llvm-prof` to merge all the `.profraw` files into a single `.profdata` file. let mergedCovFile = toolsBuildParameters.codeCovDataFile - let codeCovFileNames = try swiftTool.fileSystem.getDirectoryContents(toolsBuildParameters.codeCovPath) + let codeCovFileNames = try swiftCommandState.fileSystem.getDirectoryContents(toolsBuildParameters.codeCovPath) var llvmProfCommand = [try toolchain.getLLVMProf().pathString] llvmProfCommand += ["merge", "-sparse"] for fileName in codeCovFileNames where fileName.hasSuffix(".profraw") { @@ -343,7 +343,7 @@ final class PluginDelegate: PluginInvocationDelegate { let jsonCovFile = toolsBuildParameters.codeCovDataFile.parentDirectory.appending( component: toolsBuildParameters.codeCovDataFile.basenameWithoutExt + ".json" ) - try swiftTool.fileSystem.writeFileContents(jsonCovFile, string: jsonOutput) + try swiftCommandState.fileSystem.writeFileContents(jsonCovFile, string: jsonOutput) // Return the path of the exported code coverage data file. codeCoverageDataFile = jsonCovFile @@ -380,7 +380,7 @@ final class PluginDelegate: PluginInvocationDelegate { // while building. // Create a build system for building the target., skipping the the cache because we need the build plan. - let buildSystem = try swiftTool.createBuildSystem(explicitBuildSystem: .native, cacheBuildManifest: false) + let buildSystem = try swiftCommandState.createBuildSystem(explicitBuildSystem: .native, cacheBuildManifest: false) // Find the target in the build operation's package graph; it's an error if we don't find it. let packageGraph = try buildSystem.getPackageGraph() @@ -393,9 +393,9 @@ final class PluginDelegate: PluginInvocationDelegate { // Configure the symbol graph extractor. var symbolGraphExtractor = try SymbolGraphExtract( - fileSystem: swiftTool.fileSystem, - tool: swiftTool.getTargetToolchain().getSymbolGraphExtract(), - observabilityScope: swiftTool.observabilityScope + fileSystem: swiftCommandState.fileSystem, + tool: swiftCommandState.getTargetToolchain().getSymbolGraphExtract(), + observabilityScope: swiftCommandState.observabilityScope ) symbolGraphExtractor.skipSynthesizedMembers = !options.includeSynthesized switch options.minimumAccessLevel { @@ -423,7 +423,7 @@ final class PluginDelegate: PluginInvocationDelegate { package.identity.description, target.name ) - try swiftTool.fileSystem.removeFileTree(outputDir) + try swiftCommandState.fileSystem.removeFileTree(outputDir) // Run the symbol graph extractor on the target. let result = try symbolGraphExtractor.extractSymbolGraph( @@ -431,7 +431,7 @@ final class PluginDelegate: PluginInvocationDelegate { buildPlan: try buildSystem.buildPlan, outputRedirection: .collect, outputDirectory: outputDir, - verboseOutput: self.swiftTool.logLevel <= .info + verboseOutput: self.swiftCommandState.logLevel <= .info ) guard result.exitStatus == .terminated(code: 0) else { diff --git a/Sources/Commands/Utilities/TestingSupport.swift b/Sources/Commands/Utilities/TestingSupport.swift index 236feaa072e..335b0a9995d 100644 --- a/Sources/Commands/Utilities/TestingSupport.swift +++ b/Sources/Commands/Utilities/TestingSupport.swift @@ -31,13 +31,13 @@ enum TestingSupport { /// Note: It is a fatalError if we are not able to locate the tool. /// /// - Returns: Path to XCTestHelper tool. - static func xctestHelperPath(swiftTool: SwiftTool) throws -> AbsolutePath { + static func xctestHelperPath(swiftCommandState: SwiftCommandState) throws -> AbsolutePath { var triedPaths = [AbsolutePath]() func findXCTestHelper(swiftBuildPath: AbsolutePath) -> AbsolutePath? { // XCTestHelper tool is installed in libexec. let maybePath = swiftBuildPath.parentDirectory.parentDirectory.appending(components: "libexec", "swift", "pm", "swiftpm-xctest-helper") - if swiftTool.fileSystem.isFile(maybePath) { + if swiftCommandState.fileSystem.isFile(maybePath) { return maybePath } else { triedPaths.append(maybePath) @@ -46,7 +46,7 @@ enum TestingSupport { } if let firstCLIArgument = CommandLine.arguments.first { - let runningSwiftBuildPath = try AbsolutePath(validating: firstCLIArgument, relativeTo: swiftTool.originalWorkingDirectory) + let runningSwiftBuildPath = try AbsolutePath(validating: firstCLIArgument, relativeTo: swiftCommandState.originalWorkingDirectory) if let xctestHelperPath = findXCTestHelper(swiftBuildPath: runningSwiftBuildPath) { return xctestHelperPath } @@ -64,7 +64,7 @@ enum TestingSupport { static func getTestSuites( in testProducts: [BuiltTestProduct], - swiftTool: SwiftTool, + swiftCommandState: SwiftCommandState, enableCodeCoverage: Bool, shouldSkipBuilding: Bool, experimentalTestOutput: Bool, @@ -75,7 +75,7 @@ enum TestingSupport { $0.bundlePath, try Self.getTestSuites( fromTestAt: $0.bundlePath, - swiftTool: swiftTool, + swiftCommandState: swiftCommandState, enableCodeCoverage: enableCodeCoverage, shouldSkipBuilding: shouldSkipBuilding, experimentalTestOutput: experimentalTestOutput, @@ -97,7 +97,7 @@ enum TestingSupport { /// - Returns: Array of TestSuite static func getTestSuites( fromTestAt path: AbsolutePath, - swiftTool: SwiftTool, + swiftCommandState: SwiftCommandState, enableCodeCoverage: Bool, shouldSkipBuilding: Bool, experimentalTestOutput: Bool, @@ -107,10 +107,10 @@ enum TestingSupport { var args = [String]() #if os(macOS) let data: String = try withTemporaryFile { tempFile in - args = [try Self.xctestHelperPath(swiftTool: swiftTool).pathString, path.pathString, tempFile.path.pathString] + args = [try Self.xctestHelperPath(swiftCommandState: swiftCommandState).pathString, path.pathString, tempFile.path.pathString] let env = try Self.constructTestEnvironment( - toolchain: try swiftTool.getTargetToolchain(), - buildParameters: swiftTool.buildParametersForTest( + toolchain: try swiftCommandState.getTargetToolchain(), + buildParameters: swiftCommandState.buildParametersForTest( enableCodeCoverage: enableCodeCoverage, shouldSkipBuilding: shouldSkipBuilding, experimentalTestOutput: experimentalTestOutput, @@ -121,12 +121,12 @@ enum TestingSupport { try TSCBasic.Process.checkNonZeroExit(arguments: args, environment: env) // Read the temporary file's content. - return try swiftTool.fileSystem.readFileContents(AbsolutePath(tempFile.path)) + return try swiftCommandState.fileSystem.readFileContents(AbsolutePath(tempFile.path)) } #else let env = try Self.constructTestEnvironment( - toolchain: try swiftTool.getTargetToolchain(), - buildParameters: swiftTool.buildParametersForTest( + toolchain: try swiftCommandState.getTargetToolchain(), + buildParameters: swiftCommandState.buildParametersForTest( enableCodeCoverage: enableCodeCoverage, shouldSkipBuilding: shouldSkipBuilding, library: .xctest @@ -203,7 +203,7 @@ enum TestingSupport { } } -extension SwiftTool { +extension SwiftCommandState { func buildParametersForTest( enableCodeCoverage: Bool, enableTestability: Bool? = nil, diff --git a/Sources/CoreCommands/BuildSystemSupport.swift b/Sources/CoreCommands/BuildSystemSupport.swift index c0f48b325e3..567b2f0d186 100644 --- a/Sources/CoreCommands/BuildSystemSupport.swift +++ b/Sources/CoreCommands/BuildSystemSupport.swift @@ -23,7 +23,7 @@ import struct PackageLoading.FileRuleDescription import protocol TSCBasic.OutputByteStream private struct NativeBuildSystemFactory: BuildSystemFactory { - let swiftTool: SwiftTool + let swiftCommandState: SwiftCommandState func makeBuildSystem( explicitProduct: String?, @@ -35,37 +35,37 @@ private struct NativeBuildSystemFactory: BuildSystemFactory { logLevel: Diagnostic.Severity?, observabilityScope: ObservabilityScope? ) throws -> any BuildSystem { - let rootPackageInfo = try swiftTool.getRootPackageInformation() + let rootPackageInfo = try swiftCommandState.getRootPackageInformation() let testEntryPointPath = productsBuildParameters?.testingParameters.testProductStyle.explicitlySpecifiedEntryPointPath return try BuildOperation( - productsBuildParameters: try productsBuildParameters ?? self.swiftTool.productsBuildParameters, - toolsBuildParameters: try toolsBuildParameters ?? self.swiftTool.toolsBuildParameters, - cacheBuildManifest: cacheBuildManifest && self.swiftTool.canUseCachedBuildManifest(), + productsBuildParameters: try productsBuildParameters ?? self.swiftCommandState.productsBuildParameters, + toolsBuildParameters: try toolsBuildParameters ?? self.swiftCommandState.toolsBuildParameters, + cacheBuildManifest: cacheBuildManifest && self.swiftCommandState.canUseCachedBuildManifest(), packageGraphLoader: packageGraphLoader ?? { - try self.swiftTool.loadPackageGraph( + try self.swiftCommandState.loadPackageGraph( explicitProduct: explicitProduct, testEntryPointPath: testEntryPointPath ) }, pluginConfiguration: .init( - scriptRunner: self.swiftTool.getPluginScriptRunner(), - workDirectory: try self.swiftTool.getActiveWorkspace().location.pluginWorkingDirectory, - disableSandbox: self.swiftTool.shouldDisableSandbox + scriptRunner: self.swiftCommandState.getPluginScriptRunner(), + workDirectory: try self.swiftCommandState.getActiveWorkspace().location.pluginWorkingDirectory, + disableSandbox: self.swiftCommandState.shouldDisableSandbox ), additionalFileRules: FileRuleDescription.swiftpmFileTypes, - pkgConfigDirectories: self.swiftTool.options.locations.pkgConfigDirectories, + pkgConfigDirectories: self.swiftCommandState.options.locations.pkgConfigDirectories, dependenciesByRootPackageIdentity: rootPackageInfo.dependecies, targetsByRootPackageIdentity: rootPackageInfo.targets, - outputStream: outputStream ?? self.swiftTool.outputStream, - logLevel: logLevel ?? self.swiftTool.logLevel, - fileSystem: self.swiftTool.fileSystem, - observabilityScope: observabilityScope ?? self.swiftTool.observabilityScope) + outputStream: outputStream ?? self.swiftCommandState.outputStream, + logLevel: logLevel ?? self.swiftCommandState.logLevel, + fileSystem: self.swiftCommandState.fileSystem, + observabilityScope: observabilityScope ?? self.swiftCommandState.observabilityScope) } } #if !DISABLE_XCBUILD_SUPPORT private struct XcodeBuildSystemFactory: BuildSystemFactory { - let swiftTool: SwiftTool + let swiftCommandState: SwiftCommandState func makeBuildSystem( explicitProduct: String?, @@ -78,31 +78,31 @@ private struct XcodeBuildSystemFactory: BuildSystemFactory { observabilityScope: ObservabilityScope? ) throws -> any BuildSystem { return try XcodeBuildSystem( - buildParameters: productsBuildParameters ?? self.swiftTool.productsBuildParameters, + buildParameters: productsBuildParameters ?? self.swiftCommandState.productsBuildParameters, packageGraphLoader: packageGraphLoader ?? { - try self.swiftTool.loadPackageGraph( + try self.swiftCommandState.loadPackageGraph( explicitProduct: explicitProduct ) }, - outputStream: outputStream ?? self.swiftTool.outputStream, - logLevel: logLevel ?? self.swiftTool.logLevel, - fileSystem: self.swiftTool.fileSystem, - observabilityScope: observabilityScope ?? self.swiftTool.observabilityScope + outputStream: outputStream ?? self.swiftCommandState.outputStream, + logLevel: logLevel ?? self.swiftCommandState.logLevel, + fileSystem: self.swiftCommandState.fileSystem, + observabilityScope: observabilityScope ?? self.swiftCommandState.observabilityScope ) } } #endif -extension SwiftTool { +extension SwiftCommandState { public var defaultBuildSystemProvider: BuildSystemProvider { #if !DISABLE_XCBUILD_SUPPORT .init(providers: [ - .native: NativeBuildSystemFactory(swiftTool: self), - .xcode: XcodeBuildSystemFactory(swiftTool: self) + .native: NativeBuildSystemFactory(swiftCommandState: self), + .xcode: XcodeBuildSystemFactory(swiftCommandState: self) ]) #else .init(providers: [ - .native: NativeBuildSystemFactory(swiftTool: self), + .native: NativeBuildSystemFactory(swiftCommandState: self), ]) #endif } diff --git a/Sources/CoreCommands/CMakeLists.txt b/Sources/CoreCommands/CMakeLists.txt index 3d65ad22ce4..f9ae401890a 100644 --- a/Sources/CoreCommands/CMakeLists.txt +++ b/Sources/CoreCommands/CMakeLists.txt @@ -8,8 +8,8 @@ add_library(CoreCommands BuildSystemSupport.swift - SwiftTool.swift - SwiftToolObservabilityHandler.swift + SwiftCommandState.swift + SwiftCommandObservabilityHandler.swift Options.swift) target_link_libraries(CoreCommands PUBLIC ArgumentParser diff --git a/Sources/CoreCommands/SwiftToolObservabilityHandler.swift b/Sources/CoreCommands/SwiftCommandObservabilityHandler.swift similarity index 95% rename from Sources/CoreCommands/SwiftToolObservabilityHandler.swift rename to Sources/CoreCommands/SwiftCommandObservabilityHandler.swift index 765c2f0cd5c..3536d85240e 100644 --- a/Sources/CoreCommands/SwiftToolObservabilityHandler.swift +++ b/Sources/CoreCommands/SwiftCommandObservabilityHandler.swift @@ -18,7 +18,11 @@ import protocol TSCBasic.OutputByteStream import class TSCBasic.TerminalController import class TSCBasic.ThreadSafeOutputByteStream -public struct SwiftToolObservabilityHandler: ObservabilityHandlerProvider { +import class TSCUtility.MultiLineNinjaProgressAnimation +import class TSCUtility.NinjaProgressAnimation +import protocol TSCUtility.ProgressAnimationProtocol + +public struct SwiftCommandObservabilityHandler: ObservabilityHandlerProvider { private let outputHandler: OutputHandler public var diagnosticsHandler: DiagnosticsHandler { @@ -162,8 +166,8 @@ public struct SwiftToolObservabilityHandler: ObservabilityHandlerProvider { } } -extension SwiftToolObservabilityHandler.OutputHandler: @unchecked Sendable {} -extension SwiftToolObservabilityHandler.OutputHandler: DiagnosticsHandler {} +extension SwiftCommandObservabilityHandler.OutputHandler: @unchecked Sendable {} +extension SwiftCommandObservabilityHandler.OutputHandler: DiagnosticsHandler {} /// This type is used to write on the underlying stream. /// diff --git a/Sources/CoreCommands/SwiftTool.swift b/Sources/CoreCommands/SwiftCommandState.swift similarity index 96% rename from Sources/CoreCommands/SwiftTool.swift rename to Sources/CoreCommands/SwiftCommandState.swift index 377eff2451c..835d6100b4c 100644 --- a/Sources/CoreCommands/SwiftTool.swift +++ b/Sources/CoreCommands/SwiftCommandState.swift @@ -85,7 +85,7 @@ public protocol _SwiftCommand { var toolWorkspaceConfiguration: ToolWorkspaceConfiguration { get } var workspaceDelegateProvider: WorkspaceDelegateProvider { get } var workspaceLoaderProvider: WorkspaceLoaderProvider { get } - func buildSystemProvider(_ swiftTool: SwiftTool) throws -> BuildSystemProvider + func buildSystemProvider(_ swiftCommandState: SwiftCommandState) throws -> BuildSystemProvider } extension _SwiftCommand { @@ -95,14 +95,14 @@ extension _SwiftCommand { } public protocol SwiftCommand: ParsableCommand, _SwiftCommand { - func run(_ swiftTool: SwiftTool) throws + func run(_ swiftCommandState: SwiftCommandState) throws } extension SwiftCommand { public static var _errorLabel: String { "error" } public func run() throws { - let swiftTool = try SwiftTool( + let swiftCommandState = try SwiftCommandState( options: globalOptions, toolWorkspaceConfiguration: self.toolWorkspaceConfiguration, workspaceDelegateProvider: self.workspaceDelegateProvider, @@ -110,23 +110,23 @@ extension SwiftCommand { ) // We use this to attempt to catch misuse of the locking APIs since we only release the lock from here. - swiftTool.setNeedsLocking() + swiftCommandState.setNeedsLocking() - swiftTool.buildSystemProvider = try buildSystemProvider(swiftTool) + swiftCommandState.buildSystemProvider = try buildSystemProvider(swiftCommandState) var toolError: Error? = .none do { - try self.run(swiftTool) - if swiftTool.observabilityScope.errorsReported || swiftTool.executionStatus == .failure { + try self.run(swiftCommandState) + if swiftCommandState.observabilityScope.errorsReported || swiftCommandState.executionStatus == .failure { throw ExitCode.failure } } catch { toolError = error } - swiftTool.releaseLockIfNeeded() + swiftCommandState.releaseLockIfNeeded() // wait for all observability items to process - swiftTool.waitForObservabilityEvents(timeout: .now() + 5) + swiftCommandState.waitForObservabilityEvents(timeout: .now() + 5) if let toolError { throw toolError @@ -135,7 +135,7 @@ extension SwiftCommand { } public protocol AsyncSwiftCommand: AsyncParsableCommand, _SwiftCommand { - func run(_ swiftTool: SwiftTool) async throws + func run(_ swiftCommandState: SwiftCommandState) async throws } extension AsyncSwiftCommand { @@ -143,7 +143,7 @@ extension AsyncSwiftCommand { // FIXME: It doesn't seem great to have this be duplicated with `SwiftCommand`. public func run() async throws { - let swiftTool = try SwiftTool( + let swiftCommandState = try SwiftCommandState( options: globalOptions, toolWorkspaceConfiguration: self.toolWorkspaceConfiguration, workspaceDelegateProvider: self.workspaceDelegateProvider, @@ -151,23 +151,23 @@ extension AsyncSwiftCommand { ) // We use this to attempt to catch misuse of the locking APIs since we only release the lock from here. - swiftTool.setNeedsLocking() + swiftCommandState.setNeedsLocking() - swiftTool.buildSystemProvider = try buildSystemProvider(swiftTool) + swiftCommandState.buildSystemProvider = try buildSystemProvider(swiftCommandState) var toolError: Error? = .none do { - try await self.run(swiftTool) - if swiftTool.observabilityScope.errorsReported || swiftTool.executionStatus == .failure { + try await self.run(swiftCommandState) + if swiftCommandState.observabilityScope.errorsReported || swiftCommandState.executionStatus == .failure { throw ExitCode.failure } } catch { toolError = error } - swiftTool.releaseLockIfNeeded() + swiftCommandState.releaseLockIfNeeded() // wait for all observability items to process - swiftTool.waitForObservabilityEvents(timeout: .now() + 5) + swiftCommandState.waitForObservabilityEvents(timeout: .now() + 5) if let toolError { throw toolError @@ -175,7 +175,7 @@ extension AsyncSwiftCommand { } } -public final class SwiftTool { +public final class SwiftCommandState { #if os(Windows) // unfortunately this is needed for C callback handlers used by Windows shutdown handler static var cancellator: Cancellator? @@ -241,7 +241,7 @@ public final class SwiftTool { private var _workspace: Workspace? private var _workspaceDelegate: WorkspaceDelegate? - private let observabilityHandler: SwiftToolObservabilityHandler + private let observabilityHandler: SwiftCommandObservabilityHandler /// The observability scope to emit diagnostics event on public let observabilityScope: ObservabilityScope @@ -299,7 +299,7 @@ public final class SwiftTool { self.fileSystem = localFileSystem // first, bootstrap the observability system self.logLevel = options.logging.logLevel - self.observabilityHandler = SwiftToolObservabilityHandler(outputStream: outputStream, logLevel: self.logLevel) + self.observabilityHandler = SwiftCommandObservabilityHandler(outputStream: outputStream, logLevel: self.logLevel) let observabilitySystem = ObservabilitySystem(self.observabilityHandler) self.observabilityScope = observabilitySystem.topScope self.shouldDisableSandbox = options.security.shouldDisableSandbox @@ -1039,7 +1039,7 @@ public protocol WorkspaceLoader { // MARK: - Diagnostics -extension SwiftTool { +extension SwiftCommandState { // FIXME: deprecate these one we are further along refactoring the call sites that use it /// The stream to print standard output on. public var outputStream: OutputByteStream { diff --git a/Sources/PackageCollectionsTool/SwiftPackageCollectionsTool.swift b/Sources/PackageCollectionsCommand/PackageCollectionsCommand.swift similarity index 91% rename from Sources/PackageCollectionsTool/SwiftPackageCollectionsTool.swift rename to Sources/PackageCollectionsCommand/PackageCollectionsCommand.swift index a6fa8e95c45..d12e15240fe 100644 --- a/Sources/PackageCollectionsTool/SwiftPackageCollectionsTool.swift +++ b/Sources/PackageCollectionsCommand/PackageCollectionsCommand.swift @@ -54,7 +54,7 @@ struct JSONOptions: ParsableArguments { var json: Bool = false } -public struct SwiftPackageCollectionsTool: AsyncParsableCommand { +public struct PackageCollectionsCommand: AsyncParsableCommand { public static var configuration = CommandConfiguration( commandName: "package-collection", _superCommandName: "swift", @@ -85,8 +85,8 @@ public struct SwiftPackageCollectionsTool: AsyncParsableCommand { @OptionGroup(visibility: .hidden) var globalOptions: GlobalOptions - func run(_ swiftTool: SwiftTool) async throws { - let collections = try await with(swiftTool) { collections in + func run(_ swiftCommandState: SwiftCommandState) async throws { + let collections = try await withState(swiftCommandState) { collections in try await collections.listCollections(identifiers: nil) } @@ -106,8 +106,8 @@ public struct SwiftPackageCollectionsTool: AsyncParsableCommand { @OptionGroup(visibility: .hidden) var globalOptions: GlobalOptions - func run(_ swiftTool: SwiftTool) async throws { - let collections = try await with(swiftTool) { collections in + func run(_ swiftCommandState: SwiftCommandState) async throws { + let collections = try await withState(swiftCommandState) { collections in try await collections.refreshCollections() } print("Refreshed \(collections.count) configured package collection\(collections.count == 1 ? "" : "s").") @@ -132,11 +132,11 @@ public struct SwiftPackageCollectionsTool: AsyncParsableCommand { @OptionGroup(visibility: .hidden) var globalOptions: GlobalOptions - func run(_ swiftTool: SwiftTool) async throws { + func run(_ swiftCommandState: SwiftCommandState) async throws { let collectionURL = try url(self.collectionURL) let source = PackageCollectionsModel.CollectionSource(type: .json, url: collectionURL, skipSignatureCheck: self.skipSignatureCheck) - let collection: PackageCollectionsModel.Collection = try await with(swiftTool) { collections in + let collection: PackageCollectionsModel.Collection = try await withState(swiftCommandState) { collections in do { let userTrusted = self.trustUnsigned return try await collections.addCollection(source, order: order) { _, callback in @@ -166,11 +166,11 @@ public struct SwiftPackageCollectionsTool: AsyncParsableCommand { @OptionGroup(visibility: .hidden) var globalOptions: GlobalOptions - func run(_ swiftTool: SwiftTool) async throws { + func run(_ swiftCommandState: SwiftCommandState) async throws { let collectionURL = try url(self.collectionURL) let source = PackageCollectionsModel.CollectionSource(type: .json, url: collectionURL) - try await with(swiftTool) { collections in + try await withState(swiftCommandState) { collections in let collection = try await collections.getCollection(source) _ = try await collections.removeCollection(source) print("Removed \"\(collection.name)\" from your package collections.") @@ -200,8 +200,8 @@ public struct SwiftPackageCollectionsTool: AsyncParsableCommand { @OptionGroup(visibility: .hidden) var globalOptions: GlobalOptions - func run(_ swiftTool: SwiftTool) async throws { - try await with(swiftTool) { collections in + func run(_ swiftCommandState: SwiftCommandState) async throws { + try await withState(swiftCommandState) { collections in switch searchMethod { case .keywords: let results = try await collections.findPackages(searchQuery, collections: nil) @@ -282,8 +282,8 @@ public struct SwiftPackageCollectionsTool: AsyncParsableCommand { """ } - func run(_ swiftTool: SwiftTool) async throws { - try await with(swiftTool) { collections in + func run(_ swiftCommandState: SwiftCommandState) async throws { + try await withState(swiftCommandState) { collections in let identity = PackageIdentity(urlString: self.packageURL) do { // assume URL is for a package in an imported collection @@ -381,15 +381,18 @@ private extension JSONEncoder { } private extension ParsableCommand { - func with(_ swiftTool: SwiftTool, handler: (_ collections: PackageCollectionsProtocol) async throws -> T) async throws -> T { - _ = try? swiftTool.getActiveWorkspace(emitDeprecatedConfigurationWarning: true) + func withState( + _ swiftCommandState: SwiftCommandState, + handler: (_ collections: PackageCollectionsProtocol) async throws -> T + ) async throws -> T { + _ = try? swiftCommandState.getActiveWorkspace(emitDeprecatedConfigurationWarning: true) let collections = PackageCollections( configuration: .init( - configurationDirectory: swiftTool.sharedConfigurationDirectory, - cacheDirectory: swiftTool.sharedCacheDirectory + configurationDirectory: swiftCommandState.sharedConfigurationDirectory, + cacheDirectory: swiftCommandState.sharedCacheDirectory ), - fileSystem: swiftTool.fileSystem, - observabilityScope: swiftTool.observabilityScope + fileSystem: swiftCommandState.fileSystem, + observabilityScope: swiftCommandState.observabilityScope ) defer { do { diff --git a/Sources/PackageRegistryTool/PackageRegistryTool+Auth.swift b/Sources/PackageRegistryCommand/PackageRegistryCommand+Auth.swift similarity index 91% rename from Sources/PackageRegistryTool/PackageRegistryTool+Auth.swift rename to Sources/PackageRegistryCommand/PackageRegistryCommand+Auth.swift index 82ccf7ad294..fb15d965cb6 100644 --- a/Sources/PackageRegistryTool/PackageRegistryTool+Auth.swift +++ b/Sources/PackageRegistryCommand/PackageRegistryCommand+Auth.swift @@ -26,7 +26,7 @@ import WinSDK private func readpassword(_ prompt: String) throws -> String { enum StaticStorage { static var buffer: UnsafeMutableBufferPointer = - .allocate(capacity: SwiftPackageRegistryTool.Login.passwordBufferSize) + .allocate(capacity: PackageRegistryCommand.Login.passwordBufferSize) } let hStdIn: HANDLE = GetStdHandle(STD_INPUT_HANDLE) @@ -56,9 +56,9 @@ private func readpassword(_ prompt: String) throws -> String { ) let password = String(cString: UnsafePointer(StaticStorage.buffer.baseAddress!)) - guard password.count <= SwiftPackageRegistryTool.Login.maxPasswordLength else { - throw SwiftPackageRegistryTool.ValidationError - .credentialLengthLimitExceeded(SwiftPackageRegistryTool.Login.maxPasswordLength) + guard password.count <= PackageRegistryCommand.Login.maxPasswordLength else { + throw PackageRegistryCommand.ValidationError + .credentialLengthLimitExceeded(PackageRegistryCommand.Login.maxPasswordLength) } return password } @@ -67,7 +67,7 @@ private func readpassword(_ prompt: String) throws -> String { let password: String #if canImport(Darwin) - var buffer = [CChar](repeating: 0, count: SwiftPackageRegistryTool.Login.passwordBufferSize) + var buffer = [CChar](repeating: 0, count: PackageRegistryCommand.Login.passwordBufferSize) guard let passwordPtr = readpassphrase(prompt, &buffer, buffer.count, 0) else { throw StringError("unable to read input") @@ -80,15 +80,15 @@ private func readpassword(_ prompt: String) throws -> String { password = String(cString: getpass(prompt)) #endif - guard password.count <= SwiftPackageRegistryTool.Login.maxPasswordLength else { - throw SwiftPackageRegistryTool.ValidationError - .credentialLengthLimitExceeded(SwiftPackageRegistryTool.Login.maxPasswordLength) + guard password.count <= PackageRegistryCommand.Login.maxPasswordLength else { + throw PackageRegistryCommand.ValidationError + .credentialLengthLimitExceeded(PackageRegistryCommand.Login.maxPasswordLength) } return password } #endif -extension SwiftPackageRegistryTool { +extension PackageRegistryCommand { struct Login: AsyncSwiftCommand { static func loginURL(from registryURL: URL, loginAPIPath: String?) throws -> URL { @@ -144,12 +144,12 @@ extension SwiftPackageRegistryTool { private static let PLACEHOLDER_TOKEN_USER = "token" - func run(_ swiftTool: SwiftTool) async throws { + func run(_ swiftCommandState: SwiftCommandState) async throws { // We need to be able to read/write credentials // Make sure credentials store is available before proceeding let authorizationProvider: AuthorizationProvider? do { - authorizationProvider = try swiftTool.getRegistryAuthorizationProvider() + authorizationProvider = try swiftCommandState.getRegistryAuthorizationProvider() } catch { throw ValidationError.invalidCredentialStore(error) } @@ -159,7 +159,7 @@ extension SwiftPackageRegistryTool { } // Auth config is in user-level registries config only - let configuration = try getRegistriesConfig(swiftTool, global: true) + let configuration = try getRegistriesConfig(swiftCommandState, global: true) // compute and validate registry URL guard let registryURL = self.registryURL ?? configuration.configuration.defaultRegistry?.url else { @@ -258,7 +258,7 @@ extension SwiftPackageRegistryTool { try await registryClient.login( loginURL: loginURL, timeout: .seconds(5), - observabilityScope: swiftTool.observabilityScope, + observabilityScope: swiftCommandState.observabilityScope, callbackQueue: .sharedConcurrent ) @@ -330,9 +330,9 @@ extension SwiftPackageRegistryTool { self.url } - func run(_ swiftTool: SwiftTool) async throws { + func run(_ swiftCommandState: SwiftCommandState) async throws { // Auth config is in user-level registries config only - let configuration = try getRegistriesConfig(swiftTool, global: true) + let configuration = try getRegistriesConfig(swiftCommandState, global: true) // compute and validate registry URL guard let registryURL = self.registryURL ?? configuration.configuration.defaultRegistry?.url else { @@ -342,7 +342,7 @@ extension SwiftPackageRegistryTool { try registryURL.validateRegistryURL() // We need to be able to read/write credentials - guard let authorizationProvider = try swiftTool.getRegistryAuthorizationProvider() else { + guard let authorizationProvider = try swiftCommandState.getRegistryAuthorizationProvider() else { throw ValidationError.unknownCredentialStore } diff --git a/Sources/PackageRegistryTool/PackageRegistryTool+Publish.swift b/Sources/PackageRegistryCommand/PackageRegistryCommand+Publish.swift similarity index 95% rename from Sources/PackageRegistryTool/PackageRegistryTool+Publish.swift rename to Sources/PackageRegistryCommand/PackageRegistryCommand+Publish.swift index c698c2294b0..6e09e335d2d 100644 --- a/Sources/PackageRegistryTool/PackageRegistryTool+Publish.swift +++ b/Sources/PackageRegistryCommand/PackageRegistryCommand+Publish.swift @@ -32,7 +32,7 @@ import struct TSCBasic.SHA256 import struct TSCUtility.Version -extension SwiftPackageRegistryTool { +extension PackageRegistryCommand { struct Publish: AsyncSwiftCommand { static let metadataFilename = "package-metadata.json" @@ -88,12 +88,12 @@ extension SwiftPackageRegistryTool { @Flag(help: "Dry run only; prepare the archive and sign it but do not publish to the registry.") var dryRun: Bool = false - func run(_ swiftTool: SwiftTool) async throws { + func run(_ swiftCommandState: SwiftCommandState) async throws { // Require both local and user-level registries config - let configuration = try getRegistriesConfig(swiftTool, global: false).configuration + let configuration = try getRegistriesConfig(swiftCommandState, global: false).configuration // validate package location - let packageDirectory = try self.globalOptions.locations.packageDirectory ?? swiftTool.getPackageRoot() + let packageDirectory = try self.globalOptions.locations.packageDirectory ?? swiftCommandState.getPackageRoot() guard localFileSystem.isDirectory(packageDirectory) else { throw StringError("No package found at '\(packageDirectory)'.") } @@ -139,7 +139,7 @@ extension SwiftPackageRegistryTool { metadataLocation = .sourceTree(defaultMetadataPath) } - guard let authorizationProvider = try swiftTool.getRegistryAuthorizationProvider() else { + guard let authorizationProvider = try swiftCommandState.getRegistryAuthorizationProvider() else { throw ValidationError.unknownCredentialStore } @@ -179,9 +179,9 @@ extension SwiftPackageRegistryTool { workingDirectory: workingDirectory, mode: signingMode, signatureFormat: self.signatureFormat, - cancellator: swiftTool.cancellator, + cancellator: swiftCommandState.cancellator, fileSystem: localFileSystem, - observabilityScope: swiftTool.observabilityScope + observabilityScope: swiftCommandState.observabilityScope ) archivePath = result.archive.path archiveSignature = result.archive.signature @@ -189,15 +189,15 @@ extension SwiftPackageRegistryTool { } else { // step 2: generate source archive for the package release // step 3: signing not required - swiftTool.observabilityScope.emit(info: "archiving the source at '\(packageDirectory)'") + swiftCommandState.observabilityScope.emit(info: "archiving the source at '\(packageDirectory)'") archivePath = try PackageArchiver.archive( packageIdentity: self.packageIdentity, packageVersion: self.packageVersion, packageDirectory: packageDirectory, workingDirectory: workingDirectory, workingFilesToCopy: [], - cancellator: swiftTool.cancellator, - observabilityScope: swiftTool.observabilityScope + cancellator: swiftCommandState.cancellator, + observabilityScope: swiftCommandState.observabilityScope ) } @@ -209,7 +209,7 @@ extension SwiftPackageRegistryTool { return } - swiftTool.observabilityScope + swiftCommandState.observabilityScope .emit(info: "publishing \(self.packageIdentity) archive at '\(archivePath)' to \(registryURL)") let result = try await registryClient.publish( registryURL: registryURL, @@ -221,7 +221,7 @@ extension SwiftPackageRegistryTool { metadataSignature: metadataSignature, signatureFormat: self.signatureFormat, fileSystem: localFileSystem, - observabilityScope: swiftTool.observabilityScope, + observabilityScope: swiftCommandState.observabilityScope, callbackQueue: .sharedConcurrent ) @@ -500,7 +500,7 @@ enum PackageArchiver { try localFileSystem.writeFileContents(toBeReplacedPath, bytes: replacement) } - try SwiftPackageTool.archiveSource( + try SwiftPackageCommand.archiveSource( at: sourceDirectory, to: archivePath, fileSystem: localFileSystem, diff --git a/Sources/PackageRegistryTool/PackageRegistryTool.swift b/Sources/PackageRegistryCommand/PackageRegistryCommand.swift similarity index 86% rename from Sources/PackageRegistryTool/PackageRegistryTool.swift rename to Sources/PackageRegistryCommand/PackageRegistryCommand.swift index b387f6c1dfa..2861b53e336 100644 --- a/Sources/PackageRegistryTool/PackageRegistryTool.swift +++ b/Sources/PackageRegistryCommand/PackageRegistryCommand.swift @@ -18,7 +18,7 @@ import PackageModel import PackageRegistry import Workspace -public struct SwiftPackageRegistryTool: AsyncParsableCommand { +public struct PackageRegistryCommand: AsyncParsableCommand { public static var configuration = CommandConfiguration( commandName: "package-registry", _superCommandName: "swift", @@ -64,7 +64,7 @@ public struct SwiftPackageRegistryTool: AsyncParsableCommand { self.url } - func run(_ swiftTool: SwiftTool) async throws { + func run(_ swiftCommandState: SwiftCommandState) async throws { try self.registryURL.validateRegistryURL(allowHTTP: self.allowInsecureHTTP) let scope = try scope.map(PackageIdentity.Scope.init(validating:)) @@ -78,7 +78,7 @@ public struct SwiftPackageRegistryTool: AsyncParsableCommand { } } - let configuration = try getRegistriesConfig(swiftTool, global: self.global) + let configuration = try getRegistriesConfig(swiftCommandState, global: self.global) if self.global { try configuration.updateShared(with: set) } else { @@ -101,7 +101,7 @@ public struct SwiftPackageRegistryTool: AsyncParsableCommand { @Option(help: "Associate the registry with a given scope") var scope: String? - func run(_ swiftTool: SwiftTool) async throws { + func run(_ swiftCommandState: SwiftCommandState) async throws { let scope = try scope.map(PackageIdentity.Scope.init(validating:)) let unset: (inout RegistryConfiguration) throws -> Void = { configuration in @@ -118,7 +118,7 @@ public struct SwiftPackageRegistryTool: AsyncParsableCommand { } } - let configuration = try getRegistriesConfig(swiftTool, global: self.global) + let configuration = try getRegistriesConfig(swiftCommandState, global: self.global) if self.global { try configuration.updateShared(with: unset) } else { @@ -142,21 +142,21 @@ public struct SwiftPackageRegistryTool: AsyncParsableCommand { case credentialLengthLimitExceeded(Int) } - static func getRegistriesConfig(_ swiftTool: SwiftTool, global: Bool) throws -> Workspace.Configuration.Registries { + static func getRegistriesConfig(_ swiftCommandState: SwiftCommandState, global: Bool) throws -> Workspace.Configuration.Registries { if global { let sharedRegistriesFile = Workspace.DefaultLocations.registriesConfigurationFile( - at: swiftTool.sharedConfigurationDirectory + at: swiftCommandState.sharedConfigurationDirectory ) // Workspace not needed when working with user-level registries config return try .init( - fileSystem: swiftTool.fileSystem, + fileSystem: swiftCommandState.fileSystem, localRegistriesFile: .none, sharedRegistriesFile: sharedRegistriesFile ) } else { - let workspace = try swiftTool.getActiveWorkspace() + let workspace = try swiftCommandState.getActiveWorkspace() return try .init( - fileSystem: swiftTool.fileSystem, + fileSystem: swiftCommandState.fileSystem, localRegistriesFile: workspace.location.localRegistriesConfigurationFile, sharedRegistriesFile: workspace.location.sharedRegistriesConfigurationFile ) @@ -167,12 +167,12 @@ public struct SwiftPackageRegistryTool: AsyncParsableCommand { extension URL { func validateRegistryURL(allowHTTP: Bool = false) throws { guard self.scheme == "https" || (self.scheme == "http" && allowHTTP) else { - throw SwiftPackageRegistryTool.ValidationError.invalidURL(self) + throw PackageRegistryCommand.ValidationError.invalidURL(self) } } } -extension SwiftPackageRegistryTool.ConfigurationError: CustomStringConvertible { +extension PackageRegistryCommand.ConfigurationError: CustomStringConvertible { var description: String { switch self { case .missingScope(let scope?): @@ -183,7 +183,7 @@ extension SwiftPackageRegistryTool.ConfigurationError: CustomStringConvertible { } } -extension SwiftPackageRegistryTool.ValidationError: CustomStringConvertible { +extension PackageRegistryCommand.ValidationError: CustomStringConvertible { var description: String { switch self { case .invalidURL(let url): diff --git a/Sources/SwiftSDKTool/CMakeLists.txt b/Sources/SwiftSDKCommand/CMakeLists.txt similarity index 83% rename from Sources/SwiftSDKTool/CMakeLists.txt rename to Sources/SwiftSDKCommand/CMakeLists.txt index 607582ab432..eca68245472 100644 --- a/Sources/SwiftSDKTool/CMakeLists.txt +++ b/Sources/SwiftSDKCommand/CMakeLists.txt @@ -6,7 +6,7 @@ # See http://swift.org/LICENSE.txt for license information # See http://swift.org/CONTRIBUTORS.txt for Swift project authors -add_library(SwiftSDKTool +add_library(SwiftSDKCommand Configuration/ConfigurationSubcommand.swift Configuration/ConfigureSwiftSDK.swift Configuration/ResetConfiguration.swift @@ -16,18 +16,18 @@ add_library(SwiftSDKTool InstallSwiftSDK.swift ListSwiftSDKs.swift RemoveSwiftSDK.swift - SwiftSDKTool.swift) -target_link_libraries(SwiftSDKTool PUBLIC + SwiftSDKCommand.swift) +target_link_libraries(SwiftSDKCommand PUBLIC ArgumentParser Basics CoreCommands SPMBuildCore PackageModel) # NOTE(compnerd) workaround for CMake not setting up include flags yet -set_target_properties(SwiftSDKTool PROPERTIES +set_target_properties(SwiftSDKCommand PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY}) -install(TARGETS SwiftSDKTool +install(TARGETS SwiftSDKCommand ARCHIVE DESTINATION lib LIBRARY DESTINATION lib RUNTIME DESTINATION bin) diff --git a/Sources/SwiftSDKTool/Configuration/ConfigurationSubcommand.swift b/Sources/SwiftSDKCommand/Configuration/ConfigurationSubcommand.swift similarity index 100% rename from Sources/SwiftSDKTool/Configuration/ConfigurationSubcommand.swift rename to Sources/SwiftSDKCommand/Configuration/ConfigurationSubcommand.swift diff --git a/Sources/SwiftSDKTool/Configuration/ConfigureSwiftSDK.swift b/Sources/SwiftSDKCommand/Configuration/ConfigureSwiftSDK.swift similarity index 100% rename from Sources/SwiftSDKTool/Configuration/ConfigureSwiftSDK.swift rename to Sources/SwiftSDKCommand/Configuration/ConfigureSwiftSDK.swift diff --git a/Sources/SwiftSDKTool/Configuration/ResetConfiguration.swift b/Sources/SwiftSDKCommand/Configuration/ResetConfiguration.swift similarity index 100% rename from Sources/SwiftSDKTool/Configuration/ResetConfiguration.swift rename to Sources/SwiftSDKCommand/Configuration/ResetConfiguration.swift diff --git a/Sources/SwiftSDKTool/Configuration/SetConfiguration.swift b/Sources/SwiftSDKCommand/Configuration/SetConfiguration.swift similarity index 100% rename from Sources/SwiftSDKTool/Configuration/SetConfiguration.swift rename to Sources/SwiftSDKCommand/Configuration/SetConfiguration.swift diff --git a/Sources/SwiftSDKTool/Configuration/ShowConfiguration.swift b/Sources/SwiftSDKCommand/Configuration/ShowConfiguration.swift similarity index 100% rename from Sources/SwiftSDKTool/Configuration/ShowConfiguration.swift rename to Sources/SwiftSDKCommand/Configuration/ShowConfiguration.swift diff --git a/Sources/SwiftSDKTool/InstallSwiftSDK.swift b/Sources/SwiftSDKCommand/InstallSwiftSDK.swift similarity index 100% rename from Sources/SwiftSDKTool/InstallSwiftSDK.swift rename to Sources/SwiftSDKCommand/InstallSwiftSDK.swift diff --git a/Sources/SwiftSDKTool/ListSwiftSDKs.swift b/Sources/SwiftSDKCommand/ListSwiftSDKs.swift similarity index 100% rename from Sources/SwiftSDKTool/ListSwiftSDKs.swift rename to Sources/SwiftSDKCommand/ListSwiftSDKs.swift diff --git a/Sources/SwiftSDKTool/README.md b/Sources/SwiftSDKCommand/README.md similarity index 100% rename from Sources/SwiftSDKTool/README.md rename to Sources/SwiftSDKCommand/README.md diff --git a/Sources/SwiftSDKTool/RemoveSwiftSDK.swift b/Sources/SwiftSDKCommand/RemoveSwiftSDK.swift similarity index 100% rename from Sources/SwiftSDKTool/RemoveSwiftSDK.swift rename to Sources/SwiftSDKCommand/RemoveSwiftSDK.swift diff --git a/Sources/SwiftSDKTool/SwiftSDKTool.swift b/Sources/SwiftSDKCommand/SwiftSDKCommand.swift similarity index 95% rename from Sources/SwiftSDKTool/SwiftSDKTool.swift rename to Sources/SwiftSDKCommand/SwiftSDKCommand.swift index 3b2799ac783..286f34b26af 100644 --- a/Sources/SwiftSDKTool/SwiftSDKTool.swift +++ b/Sources/SwiftSDKCommand/SwiftSDKCommand.swift @@ -13,7 +13,7 @@ import ArgumentParser import Basics -public struct SwiftSDKTool: AsyncParsableCommand { +public struct SwiftSDKCommand: AsyncParsableCommand { public static let configuration = CommandConfiguration( commandName: "experimental-sdk", _superCommandName: "swift", diff --git a/Sources/SwiftSDKTool/SwiftSDKSubcommand.swift b/Sources/SwiftSDKCommand/SwiftSDKSubcommand.swift similarity index 96% rename from Sources/SwiftSDKTool/SwiftSDKSubcommand.swift rename to Sources/SwiftSDKCommand/SwiftSDKSubcommand.swift index c721e918de9..ce56b9bb553 100644 --- a/Sources/SwiftSDKTool/SwiftSDKSubcommand.swift +++ b/Sources/SwiftSDKCommand/SwiftSDKSubcommand.swift @@ -55,7 +55,7 @@ extension SwiftSDKSubcommand { } public func run() async throws { - let observabilityHandler = SwiftToolObservabilityHandler(outputStream: stdoutStream, logLevel: .info) + let observabilityHandler = SwiftCommandObservabilityHandler(outputStream: stdoutStream, logLevel: .info) let observabilitySystem = ObservabilitySystem(observabilityHandler) let observabilityScope = observabilitySystem.topScope let swiftSDKsDirectory = try self.getOrCreateSwiftSDKsDirectory() diff --git a/Sources/swift-build/main.swift b/Sources/swift-build/main.swift index dbf30ff272b..9600d0998cf 100644 --- a/Sources/swift-build/main.swift +++ b/Sources/swift-build/main.swift @@ -12,4 +12,4 @@ import Commands -SwiftBuildTool.main() +SwiftBuildCommand.main() diff --git a/Sources/swift-experimental-sdk/CMakeLists.txt b/Sources/swift-experimental-sdk/CMakeLists.txt index b7d9cace5f8..edad12be83b 100644 --- a/Sources/swift-experimental-sdk/CMakeLists.txt +++ b/Sources/swift-experimental-sdk/CMakeLists.txt @@ -9,7 +9,7 @@ add_executable(swift-experimental-sdk Entrypoint.swift) target_link_libraries(swift-experimental-sdk PRIVATE - SwiftSDKTool) + SwiftSDKCommand) target_compile_options(swift-experimental-sdk PRIVATE -parse-as-library) diff --git a/Sources/swift-experimental-sdk/Entrypoint.swift b/Sources/swift-experimental-sdk/Entrypoint.swift index 385c109192e..71d84e8443d 100644 --- a/Sources/swift-experimental-sdk/Entrypoint.swift +++ b/Sources/swift-experimental-sdk/Entrypoint.swift @@ -10,11 +10,11 @@ // //===----------------------------------------------------------------------===// -import SwiftSDKTool +import SwiftSDKCommand @main struct Entrypoint { static func main() async { - await SwiftSDKTool.main() + await SwiftSDKCommand.main() } } diff --git a/Sources/swift-package-collection/Entrypoint.swift b/Sources/swift-package-collection/Entrypoint.swift index 2ad651a0f4f..be7f68312ed 100644 --- a/Sources/swift-package-collection/Entrypoint.swift +++ b/Sources/swift-package-collection/Entrypoint.swift @@ -11,12 +11,11 @@ //===----------------------------------------------------------------------===// import Commands -import PackageCollectionsTool +import PackageCollectionsCommand @main struct Entrypoint { static func main() async { - await SwiftPackageCollectionsTool.main() + await PackageCollectionsCommand.main() } } - diff --git a/Sources/swift-package-manager/SwiftPM.swift b/Sources/swift-package-manager/SwiftPM.swift index e441a621873..750597f5ed3 100644 --- a/Sources/swift-package-manager/SwiftPM.swift +++ b/Sources/swift-package-manager/SwiftPM.swift @@ -12,9 +12,9 @@ import Basics import Commands -import SwiftSDKTool -import PackageCollectionsTool -import PackageRegistryTool +import SwiftSDKCommand +import PackageCollectionsCommand +import PackageRegistryCommand let firstArg = CommandLine.arguments[0] let execName = (try? AbsolutePath(validating: firstArg).basenameWithoutExt) ?? @@ -25,19 +25,19 @@ struct SwiftPM { static func main() async { switch execName { case "swift-package": - await SwiftPackageTool.main() + await SwiftPackageCommand.main() case "swift-build": - SwiftBuildTool.main() + SwiftBuildCommand.main() case "swift-experimental-sdk": - await SwiftSDKTool.main() + await SwiftSDKCommand.main() case "swift-test": - SwiftTestTool.main() + SwiftTestCommand.main() case "swift-run": - SwiftRunTool.main() + SwiftRunCommand.main() case "swift-package-collection": - await SwiftPackageCollectionsTool.main() + await PackageCollectionsCommand.main() case "swift-package-registry": - await SwiftPackageRegistryTool.main() + await PackageRegistryCommand.main() default: fatalError("swift-package-manager launched with unexpected name: \(execName ?? "(unknown)")") } diff --git a/Sources/swift-package-registry/runner.swift b/Sources/swift-package-registry/runner.swift index 50de66b7f43..654123014ee 100644 --- a/Sources/swift-package-registry/runner.swift +++ b/Sources/swift-package-registry/runner.swift @@ -11,11 +11,11 @@ //===----------------------------------------------------------------------===// import Commands -import PackageRegistryTool +import PackageRegistryCommand @main struct Runner { static func main() async { - await SwiftPackageRegistryTool.main() + await PackageRegistryCommand.main() } } diff --git a/Sources/swift-package/Entrypoint.swift b/Sources/swift-package/Entrypoint.swift index 3498f0a5bd5..d6113fbbaef 100644 --- a/Sources/swift-package/Entrypoint.swift +++ b/Sources/swift-package/Entrypoint.swift @@ -15,6 +15,6 @@ import Commands @main struct Entrypoint { static func main() async { - await SwiftPackageTool.main() + await SwiftPackageCommand.main() } } diff --git a/Sources/swift-run/main.swift b/Sources/swift-run/main.swift index f0f3ca216d2..0f624378052 100644 --- a/Sources/swift-run/main.swift +++ b/Sources/swift-run/main.swift @@ -12,4 +12,4 @@ import Commands -SwiftRunTool.main() +SwiftRunCommand.main() diff --git a/Sources/swift-test/main.swift b/Sources/swift-test/main.swift index 9d76e0158eb..01cef92cb73 100644 --- a/Sources/swift-test/main.swift +++ b/Sources/swift-test/main.swift @@ -12,4 +12,4 @@ import Commands -SwiftTestTool.main() +SwiftTestCommand.main() diff --git a/Tests/CommandsTests/BuildToolTests.swift b/Tests/CommandsTests/BuildCommandTests.swift similarity index 99% rename from Tests/CommandsTests/BuildToolTests.swift rename to Tests/CommandsTests/BuildCommandTests.swift index 6ea803fc1eb..3221369719f 100644 --- a/Tests/CommandsTests/BuildToolTests.swift +++ b/Tests/CommandsTests/BuildCommandTests.swift @@ -29,7 +29,7 @@ struct BuildResult { let moduleContents: [String] } -final class BuildToolTests: CommandsTestCase { +final class BuildCommandTests: CommandsTestCase { @discardableResult private func execute( _ args: [String] = [], @@ -624,7 +624,7 @@ final class BuildToolTests: CommandsTestCase { ) XCTAssertNoMatch(buildResult.stdout, .contains("codesign --force --sign - --entitlements")) - XCTAssertMatch(buildResult.stderr, .contains(SwiftTool.entitlementsMacOSWarning)) + XCTAssertMatch(buildResult.stderr, .contains(SwiftCommandState.entitlementsMacOSWarning)) buildResult = try self.build( ["--enable-get-task-allow-entitlement", "-v"], @@ -633,7 +633,7 @@ final class BuildToolTests: CommandsTestCase { ) XCTAssertNoMatch(buildResult.stdout, .contains("codesign --force --sign - --entitlements")) - XCTAssertMatch(buildResult.stderr, .contains(SwiftTool.entitlementsMacOSWarning)) + XCTAssertMatch(buildResult.stderr, .contains(SwiftCommandState.entitlementsMacOSWarning)) #endif buildResult = try self.build(["-c", "release", "-v"], packagePath: fixturePath, isRelease: true) diff --git a/Tests/CommandsTests/PackageToolTests.swift b/Tests/CommandsTests/PackageCommandTests.swift similarity index 99% rename from Tests/CommandsTests/PackageToolTests.swift rename to Tests/CommandsTests/PackageCommandTests.swift index 4d91794c8bd..c9b2f34b8a4 100644 --- a/Tests/CommandsTests/PackageToolTests.swift +++ b/Tests/CommandsTests/PackageCommandTests.swift @@ -28,7 +28,7 @@ import class TSCBasic.InMemoryFileSystem import enum TSCBasic.JSON import class TSCBasic.Process -final class PackageToolTests: CommandsTestCase { +final class PackageCommandTests: CommandsTestCase { @discardableResult private func execute( _ args: [String] = [], @@ -493,7 +493,7 @@ final class PackageToolTests: CommandsTestCase { // Returns symbol graph with or without pretty printing. private func symbolGraph(atPath path: AbsolutePath, withPrettyPrinting: Bool, file: StaticString = #file, line: UInt = #line) throws -> Data? { - let tool = try SwiftTool.createSwiftToolForTest(options: GlobalOptions.parse(["--package-path", path.pathString])) + let tool = try SwiftCommandState.makeMockState(options: GlobalOptions.parse(["--package-path", path.pathString])) let symbolGraphExtractorPath = try tool.getTargetToolchain().getSymbolGraphExtract() let arguments = withPrettyPrinting ? ["dump-symbol-graph", "--pretty-print"] : ["dump-symbol-graph"] @@ -638,7 +638,7 @@ final class PackageToolTests: CommandsTestCase { XCTAssertNoDiagnostics(observability.diagnostics) let output = BufferedOutputByteStream() - SwiftPackageTool.ShowDependencies.dumpDependenciesOf( + SwiftPackageCommand.ShowDependencies.dumpDependenciesOf( rootPackage: graph.rootPackages[graph.rootPackages.startIndex], mode: .dot, on: output diff --git a/Tests/CommandsTests/PackageRegistryToolTests.swift b/Tests/CommandsTests/PackageRegistryCommandTests.swift similarity index 97% rename from Tests/CommandsTests/PackageRegistryToolTests.swift rename to Tests/CommandsTests/PackageRegistryCommandTests.swift index 31c803253e9..90e842413ad 100644 --- a/Tests/CommandsTests/PackageRegistryToolTests.swift +++ b/Tests/CommandsTests/PackageRegistryCommandTests.swift @@ -16,7 +16,7 @@ import Foundation import PackageLoading import PackageModel import PackageRegistry -@testable import PackageRegistryTool +@testable import PackageRegistryCommand import PackageSigning import SPMTestSupport import TSCclibc // for SPM_posix_spawn_file_actions_addchdir_np_supported @@ -29,7 +29,7 @@ import struct TSCBasic.ProcessResult let defaultRegistryBaseURL = URL("https://packages.example.com") let customRegistryBaseURL = URL("https://custom.packages.example.com") -final class PackageRegistryToolTests: CommandsTestCase { +final class PackageRegistryCommandTests: CommandsTestCase { @discardableResult private func execute( _ args: [String], @@ -338,7 +338,7 @@ final class PackageRegistryToolTests: CommandsTestCase { let observability = ObservabilitySystem.makeForTesting() let packageIdentity = PackageIdentity.plain("org.package") - let metadataFilename = SwiftPackageRegistryTool.Publish.metadataFilename + let metadataFilename = PackageRegistryCommand.Publish.metadataFilename // git repo try await withTemporaryDirectory { temporaryDirectory in @@ -650,7 +650,7 @@ final class PackageRegistryToolTests: CommandsTestCase { let workingDirectory = temporaryDirectory.appending(component: UUID().uuidString) try localFileSystem.createDirectory(workingDirectory) - let metadataPath = packageDirectory.appending(SwiftPackageRegistryTool.Publish.metadataFilename) + let metadataPath = packageDirectory.appending(PackageRegistryCommand.Publish.metadataFilename) try localFileSystem.writeFileContents(metadataPath, string: "{}") try SwiftPM.Registry.execute( @@ -889,7 +889,7 @@ final class PackageRegistryToolTests: CommandsTestCase { let workingDirectory = temporaryDirectory.appending(component: UUID().uuidString) try localFileSystem.createDirectory(workingDirectory) - let metadataPath = packageDirectory.appending(SwiftPackageRegistryTool.Publish.metadataFilename) + let metadataPath = packageDirectory.appending(PackageRegistryCommand.Publish.metadataFilename) try localFileSystem.writeFileContents(metadataPath, string: "{}") let certificatePath = temporaryDirectory.appending(component: "certificate.cer") @@ -1087,17 +1087,17 @@ final class PackageRegistryToolTests: CommandsTestCase { func testCreateLoginURL() { let registryURL = URL(string: "https://packages.example.com")! - XCTAssertEqual(try SwiftPackageRegistryTool.Login.loginURL(from: registryURL, loginAPIPath: nil).absoluteString, "https://packages.example.com/login") + XCTAssertEqual(try PackageRegistryCommand.Login.loginURL(from: registryURL, loginAPIPath: nil).absoluteString, "https://packages.example.com/login") - XCTAssertEqual(try SwiftPackageRegistryTool.Login.loginURL(from: registryURL, loginAPIPath: "/secret-sign-in").absoluteString, "https://packages.example.com/secret-sign-in") + XCTAssertEqual(try PackageRegistryCommand.Login.loginURL(from: registryURL, loginAPIPath: "/secret-sign-in").absoluteString, "https://packages.example.com/secret-sign-in") } func testCreateLoginURLMaintainsPort() { let registryURL = URL(string: "https://packages.example.com:8081")! - XCTAssertEqual(try SwiftPackageRegistryTool.Login.loginURL(from: registryURL, loginAPIPath: nil).absoluteString, "https://packages.example.com:8081/login") + XCTAssertEqual(try PackageRegistryCommand.Login.loginURL(from: registryURL, loginAPIPath: nil).absoluteString, "https://packages.example.com:8081/login") - XCTAssertEqual(try SwiftPackageRegistryTool.Login.loginURL(from: registryURL, loginAPIPath: "/secret-sign-in").absoluteString, "https://packages.example.com:8081/secret-sign-in") + XCTAssertEqual(try PackageRegistryCommand.Login.loginURL(from: registryURL, loginAPIPath: "/secret-sign-in").absoluteString, "https://packages.example.com:8081/secret-sign-in") } func testValidateRegistryURL() throws { diff --git a/Tests/CommandsTests/RunToolTests.swift b/Tests/CommandsTests/RunCommandTests.swift similarity index 99% rename from Tests/CommandsTests/RunToolTests.swift rename to Tests/CommandsTests/RunCommandTests.swift index d2089e023c2..7661902cbff 100644 --- a/Tests/CommandsTests/RunToolTests.swift +++ b/Tests/CommandsTests/RunCommandTests.swift @@ -17,8 +17,7 @@ import XCTest import class TSCBasic.Process -final class RunToolTests: CommandsTestCase { - +final class RunCommandTests: CommandsTestCase { private func execute( _ args: [String] = [], packagePath: AbsolutePath? = nil diff --git a/Tests/CommandsTests/SwiftToolTests.swift b/Tests/CommandsTests/SwiftCommandStateTests.swift similarity index 90% rename from Tests/CommandsTests/SwiftToolTests.swift rename to Tests/CommandsTests/SwiftCommandStateTests.swift index cc5c291de0e..cbd580fa1fc 100644 --- a/Tests/CommandsTests/SwiftToolTests.swift +++ b/Tests/CommandsTests/SwiftCommandStateTests.swift @@ -23,13 +23,13 @@ import class TSCBasic.InMemoryFileSystem import protocol TSCBasic.OutputByteStream import var TSCBasic.stderrStream -final class SwiftToolTests: CommandsTestCase { +final class SwiftCommandStateTests: CommandsTestCase { func testVerbosityLogLevel() throws { try fixture(name: "Miscellaneous/Simple") { fixturePath in do { let outputStream = BufferedOutputByteStream() let options = try GlobalOptions.parse(["--package-path", fixturePath.pathString]) - let tool = try SwiftTool.createSwiftToolForTest(outputStream: outputStream, options: options) + let tool = try SwiftCommandState.makeMockState(outputStream: outputStream, options: options) XCTAssertEqual(tool.logLevel, .warning) tool.observabilityScope.emit(error: "error") @@ -48,7 +48,7 @@ final class SwiftToolTests: CommandsTestCase { do { let outputStream = BufferedOutputByteStream() let options = try GlobalOptions.parse(["--package-path", fixturePath.pathString, "--verbose"]) - let tool = try SwiftTool.createSwiftToolForTest(outputStream: outputStream, options: options) + let tool = try SwiftCommandState.makeMockState(outputStream: outputStream, options: options) XCTAssertEqual(tool.logLevel, .info) tool.observabilityScope.emit(error: "error") @@ -67,7 +67,7 @@ final class SwiftToolTests: CommandsTestCase { do { let outputStream = BufferedOutputByteStream() let options = try GlobalOptions.parse(["--package-path", fixturePath.pathString, "-v"]) - let tool = try SwiftTool.createSwiftToolForTest(outputStream: outputStream, options: options) + let tool = try SwiftCommandState.makeMockState(outputStream: outputStream, options: options) XCTAssertEqual(tool.logLevel, .info) tool.observabilityScope.emit(error: "error") @@ -86,7 +86,7 @@ final class SwiftToolTests: CommandsTestCase { do { let outputStream = BufferedOutputByteStream() let options = try GlobalOptions.parse(["--package-path", fixturePath.pathString, "--very-verbose"]) - let tool = try SwiftTool.createSwiftToolForTest(outputStream: outputStream, options: options) + let tool = try SwiftCommandState.makeMockState(outputStream: outputStream, options: options) XCTAssertEqual(tool.logLevel, .debug) tool.observabilityScope.emit(error: "error") @@ -105,7 +105,7 @@ final class SwiftToolTests: CommandsTestCase { do { let outputStream = BufferedOutputByteStream() let options = try GlobalOptions.parse(["--package-path", fixturePath.pathString, "--vv"]) - let tool = try SwiftTool.createSwiftToolForTest(outputStream: outputStream, options: options) + let tool = try SwiftCommandState.makeMockState(outputStream: outputStream, options: options) XCTAssertEqual(tool.logLevel, .debug) tool.observabilityScope.emit(error: "error") @@ -124,7 +124,7 @@ final class SwiftToolTests: CommandsTestCase { do { let outputStream = BufferedOutputByteStream() let options = try GlobalOptions.parse(["--package-path", fixturePath.pathString, "--quiet"]) - let tool = try SwiftTool.createSwiftToolForTest(outputStream: outputStream, options: options) + let tool = try SwiftCommandState.makeMockState(outputStream: outputStream, options: options) XCTAssertEqual(tool.logLevel, .error) tool.observabilityScope.emit(error: "error") @@ -143,7 +143,7 @@ final class SwiftToolTests: CommandsTestCase { do { let outputStream = BufferedOutputByteStream() let options = try GlobalOptions.parse(["--package-path", fixturePath.pathString, "-q"]) - let tool = try SwiftTool.createSwiftToolForTest(outputStream: outputStream, options: options) + let tool = try SwiftCommandState.makeMockState(outputStream: outputStream, options: options) XCTAssertEqual(tool.logLevel, .error) tool.observabilityScope.emit(error: "error") @@ -174,7 +174,7 @@ final class SwiftToolTests: CommandsTestCase { ) let options = try GlobalOptions.parse(["--package-path", fixturePath.pathString, "--netrc-file", customPath.pathString]) - let tool = try SwiftTool.createSwiftToolForTest(options: options) + let tool = try SwiftCommandState.makeMockState(options: options) let authorizationProvider = try tool.getAuthorizationProvider() as? CompositeAuthorizationProvider let netrcProviders = authorizationProvider?.providers.compactMap { $0 as? NetrcAuthorizationProvider } ?? [] @@ -209,7 +209,7 @@ final class SwiftToolTests: CommandsTestCase { ) let options = try GlobalOptions.parse(["--package-path", fixturePath.pathString, "--netrc-file", customPath.pathString]) - let tool = try SwiftTool.createSwiftToolForTest(options: options) + let tool = try SwiftCommandState.makeMockState(options: options) // There is only one AuthorizationProvider depending on platform #if canImport(Security) @@ -253,7 +253,7 @@ final class SwiftToolTests: CommandsTestCase { /* -debug-info-format dwarf */ let explicitDwarfOptions = try GlobalOptions.parse(["--triple", "x86_64-unknown-windows-msvc", "-debug-info-format", "dwarf"]) - let explicitDwarf = try SwiftTool.createSwiftToolForTest(options: explicitDwarfOptions) + let explicitDwarf = try SwiftCommandState.makeMockState(options: explicitDwarfOptions) plan = try BuildPlan( productsBuildParameters: explicitDwarf.productsBuildParameters, toolsBuildParameters: explicitDwarf.toolsBuildParameters, @@ -267,7 +267,7 @@ final class SwiftToolTests: CommandsTestCase { /* -debug-info-format codeview */ let explicitCodeViewOptions = try GlobalOptions.parse(["--triple", "x86_64-unknown-windows-msvc", "-debug-info-format", "codeview"]) - let explicitCodeView = try SwiftTool.createSwiftToolForTest(options: explicitCodeViewOptions) + let explicitCodeView = try SwiftCommandState.makeMockState(options: explicitCodeViewOptions) plan = try BuildPlan( productsBuildParameters: explicitCodeView.productsBuildParameters, @@ -279,11 +279,11 @@ final class SwiftToolTests: CommandsTestCase { try XCTAssertMatch(plan.buildProducts.compactMap { $0 as? Build.ProductBuildDescription }.first?.linkArguments() ?? [], [.anySequence, "-g", "-debug-info-format=codeview", "-Xlinker", "-debug"]) - // Explicitly pass Linux as when the SwiftTool tests are enabled on + // Explicitly pass Linux as when the `SwiftCommandState` tests are enabled on // Windows, this would fail otherwise as CodeView is supported on the // native host. let unsupportedCodeViewOptions = try GlobalOptions.parse(["--triple", "x86_64-unknown-linux-gnu", "-debug-info-format", "codeview"]) - let unsupportedCodeView = try SwiftTool.createSwiftToolForTest(options: unsupportedCodeViewOptions) + let unsupportedCodeView = try SwiftCommandState.makeMockState(options: unsupportedCodeViewOptions) XCTAssertThrowsError(try unsupportedCodeView.productsBuildParameters) { XCTAssertEqual($0 as? StringError, StringError("CodeView debug information is currently not supported on linux")) @@ -291,7 +291,7 @@ final class SwiftToolTests: CommandsTestCase { /* <> */ let implicitDwarfOptions = try GlobalOptions.parse(["--triple", "x86_64-unknown-windows-msvc"]) - let implicitDwarf = try SwiftTool.createSwiftToolForTest(options: implicitDwarfOptions) + let implicitDwarf = try SwiftCommandState.makeMockState(options: implicitDwarfOptions) plan = try BuildPlan( productsBuildParameters: implicitDwarf.productsBuildParameters, toolsBuildParameters: implicitDwarf.toolsBuildParameters, @@ -304,7 +304,7 @@ final class SwiftToolTests: CommandsTestCase { /* -debug-info-format none */ let explicitNoDebugInfoOptions = try GlobalOptions.parse(["--triple", "x86_64-unknown-windows-msvc", "-debug-info-format", "none"]) - let explicitNoDebugInfo = try SwiftTool.createSwiftToolForTest(options: explicitNoDebugInfoOptions) + let explicitNoDebugInfo = try SwiftCommandState.makeMockState(options: explicitNoDebugInfoOptions) plan = try BuildPlan( productsBuildParameters: explicitNoDebugInfo.productsBuildParameters, toolsBuildParameters: explicitNoDebugInfo.toolsBuildParameters, @@ -317,17 +317,17 @@ final class SwiftToolTests: CommandsTestCase { } } -extension SwiftTool { - static func createSwiftToolForTest( +extension SwiftCommandState { + static func makeMockState( outputStream: OutputByteStream = stderrStream, options: GlobalOptions - ) throws -> SwiftTool { - return try SwiftTool( + ) throws -> SwiftCommandState { + return try SwiftCommandState( outputStream: outputStream, options: options, toolWorkspaceConfiguration: .init(shouldInstallSignalHandlers: false), workspaceDelegateProvider: { - ToolWorkspaceDelegate( + CommandWorkspaceDelegate( observabilityScope: $0, outputHandler: $1, progressHandler: $2, diff --git a/Tests/CommandsTests/TestToolTests.swift b/Tests/CommandsTests/TestCommandTests.swift similarity index 99% rename from Tests/CommandsTests/TestToolTests.swift rename to Tests/CommandsTests/TestCommandTests.swift index 72e1a99b51d..2f485ded9a4 100644 --- a/Tests/CommandsTests/TestToolTests.swift +++ b/Tests/CommandsTests/TestCommandTests.swift @@ -16,7 +16,7 @@ import PackageModel import SPMTestSupport import XCTest -final class TestToolTests: CommandsTestCase { +final class TestCommandTests: CommandsTestCase { private func execute(_ args: [String], packagePath: AbsolutePath? = nil) throws -> (stdout: String, stderr: String) { return try SwiftPM.Test.execute(args, packagePath: packagePath) From 8ec22d7413334d1f42e54793a1dbe8fb99b32148 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Mon, 26 Feb 2024 16:40:33 +0000 Subject: [PATCH 027/159] Allow `swift-build` to have `async` entrypoint (#7369) Due to a bug in Swift 5.9, we can't have multiple executables with `async` entrypoints linked into test products. This was the reason why we weren't able to make `swift-build` entrypoint `async`. Since we have a single `swift-package-manager` executable anyway, we can link that one into tests instead, and pass "simulated" executable name via an environment variable. This allows us to work around the bug and start using structured concurrency from `swift-build` entrypoint. This change is a prerequisite for https://github.com/apple/swift-package-manager/pull/7367. --- Package.swift | 13 +++--------- Sources/Commands/SwiftBuildCommand.swift | 4 ++-- Sources/Commands/SwiftRunCommand.swift | 21 ++++++++++++++----- Sources/SPMTestSupport/SwiftPMProduct.swift | 11 +++++----- Sources/swift-build/CMakeLists.txt | 5 ++++- .../Entrypoint.swift} | 7 ++++++- Sources/swift-package-manager/SwiftPM.swift | 20 +++++++++++++++--- Sources/swift-run/CMakeLists.txt | 5 ++++- .../main.swift => swift-run/Entrypoint.swift} | 7 ++++++- Tests/CommandsTests/RunCommandTests.swift | 5 +++++ 10 files changed, 69 insertions(+), 29 deletions(-) rename Sources/{swift-run/main.swift => swift-build/Entrypoint.swift} (83%) rename Sources/{swift-build/main.swift => swift-run/Entrypoint.swift} (83%) diff --git a/Package.swift b/Package.swift index 14110d94038..8ecc9074dc0 100644 --- a/Package.swift +++ b/Package.swift @@ -681,9 +681,7 @@ package.targets.append(contentsOf: [ .testTarget( name: "FunctionalPerformanceTests", dependencies: [ - "swift-build", - "swift-package", - "swift-test", + "swift-package-manager", "SPMTestSupport" ] ), @@ -695,9 +693,7 @@ if ProcessInfo.processInfo.environment["SWIFTCI_DISABLE_SDK_DEPENDENT_TESTS"] == .testTarget( name: "FunctionalTests", dependencies: [ - "swift-build", - "swift-package", - "swift-test", + "swift-package-manager", "PackageModel", "SPMTestSupport" ] @@ -713,10 +709,7 @@ if ProcessInfo.processInfo.environment["SWIFTCI_DISABLE_SDK_DEPENDENT_TESTS"] == .testTarget( name: "CommandsTests", dependencies: [ - "swift-build", - "swift-package", - "swift-test", - "swift-run", + "swift-package-manager", "Basics", "Build", "Commands", diff --git a/Sources/Commands/SwiftBuildCommand.swift b/Sources/Commands/SwiftBuildCommand.swift index 13c2d658621..a29eca9d0e1 100644 --- a/Sources/Commands/SwiftBuildCommand.swift +++ b/Sources/Commands/SwiftBuildCommand.swift @@ -95,7 +95,7 @@ struct BuildCommandOptions: ParsableArguments { } /// swift-build command namespace -public struct SwiftBuildCommand: SwiftCommand { +public struct SwiftBuildCommand: AsyncSwiftCommand { public static var configuration = CommandConfiguration( commandName: "build", _superCommandName: "swift", @@ -110,7 +110,7 @@ public struct SwiftBuildCommand: SwiftCommand { @OptionGroup() var options: BuildCommandOptions - public func run(_ swiftCommandState: SwiftCommandState) throws { + public func run(_ swiftCommandState: SwiftCommandState) async throws { if options.shouldPrintBinPath { return try print(swiftCommandState.productsBuildParameters.buildPath.description) } diff --git a/Sources/Commands/SwiftRunCommand.swift b/Sources/Commands/SwiftRunCommand.swift index bbf480ab23e..de7e9077090 100644 --- a/Sources/Commands/SwiftRunCommand.swift +++ b/Sources/Commands/SwiftRunCommand.swift @@ -90,7 +90,7 @@ struct RunCommandOptions: ParsableArguments { } /// swift-run command namespace -public struct SwiftRunCommand: SwiftCommand { +public struct SwiftRunCommand: AsyncSwiftCommand { public static var configuration = CommandConfiguration( commandName: "run", _superCommandName: "swift", @@ -109,7 +109,7 @@ public struct SwiftRunCommand: SwiftCommand { return .init(wantsREPLProduct: options.mode == .repl) } - public func run(_ swiftCommandState: SwiftCommandState) throws { + public func run(_ swiftCommandState: SwiftCommandState) async throws { if options.shouldBuildTests && options.shouldSkipBuild { swiftCommandState.observabilityScope.emit( .mutuallyExclusiveArgumentsError(arguments: ["--build-tests", "--skip-build"]) @@ -284,9 +284,20 @@ public struct SwiftRunCommand: SwiftCommand { /// A safe wrapper of TSCBasic.exec. private func execute(path: String, args: [String]) throws -> Never { #if !os(Windows) - // On platforms other than Windows, signal(SIGINT, SIG_IGN) is used for handling SIGINT by DispatchSourceSignal, - // but this process is about to be replaced by exec, so SIG_IGN must be returned to default. - signal(SIGINT, SIG_DFL) + // Dispatch will disable almost all asynchronous signals on its worker threads, and this is called from `async` + // context. To correctly `exec` a freshly built binary, we will need to: + // 1. reset the signal masks + for i in 1.. AbsolutePath { @@ -114,11 +114,12 @@ extension SwiftPM { #endif // FIXME: We use this private environment variable hack to be able to // create special conditions in swift-build for swiftpm tests. - environment["SWIFTPM_TESTS_MODULECACHE"] = xctestBinaryPath.parentDirectory.pathString - + environment["SWIFTPM_TESTS_MODULECACHE"] = self.xctestBinaryPath.parentDirectory.pathString + // Unset the internal env variable that allows skipping certain tests. environment["_SWIFTPM_SKIP_TESTS_LIST"] = nil - + environment["SWIFTPM_EXEC_NAME"] = self.executableName + for (key, value) in env ?? [:] { environment[key] = value } diff --git a/Sources/swift-build/CMakeLists.txt b/Sources/swift-build/CMakeLists.txt index 4488799b1f5..936298a6fad 100644 --- a/Sources/swift-build/CMakeLists.txt +++ b/Sources/swift-build/CMakeLists.txt @@ -7,9 +7,12 @@ # See http://swift.org/CONTRIBUTORS.txt for Swift project authors add_executable(swift-build - main.swift) + Entrypoint.swift) target_link_libraries(swift-build PRIVATE Commands) +target_compile_options(swift-build PRIVATE + -parse-as-library) + install(TARGETS swift-build DESTINATION bin) diff --git a/Sources/swift-run/main.swift b/Sources/swift-build/Entrypoint.swift similarity index 83% rename from Sources/swift-run/main.swift rename to Sources/swift-build/Entrypoint.swift index 0f624378052..0dcb53302ec 100644 --- a/Sources/swift-run/main.swift +++ b/Sources/swift-build/Entrypoint.swift @@ -12,4 +12,9 @@ import Commands -SwiftRunCommand.main() +@main +struct Entrypoint { + static func main() async { + await SwiftBuildCommand.main() + } +} diff --git a/Sources/swift-package-manager/SwiftPM.swift b/Sources/swift-package-manager/SwiftPM.swift index 750597f5ed3..3e3d7a106ee 100644 --- a/Sources/swift-package-manager/SwiftPM.swift +++ b/Sources/swift-package-manager/SwiftPM.swift @@ -17,23 +17,35 @@ import PackageCollectionsCommand import PackageRegistryCommand let firstArg = CommandLine.arguments[0] -let execName = (try? AbsolutePath(validating: firstArg).basenameWithoutExt) ?? +let baseNameWithoutExtension = (try? AbsolutePath(validating: firstArg).basenameWithoutExt) ?? (try? RelativePath(validating: firstArg).basenameWithoutExt) @main struct SwiftPM { static func main() async { + // Workaround a bug in Swift 5.9, where multiple executables with an `async` main entrypoint can't be linked + // into the same test bundle. We're then linking single `swift-package-manager` binary instead and passing + // executable name via `SWIFTPM_EXEC_NAME`. + if baseNameWithoutExtension == "swift-package-manager" { + await main(execName: EnvironmentVariables.process()["SWIFTPM_EXEC_NAME"]) + } else { + await main(execName: baseNameWithoutExtension) + } + } + + @discardableResult + private static func main(execName: String?) async -> Bool { switch execName { case "swift-package": await SwiftPackageCommand.main() case "swift-build": - SwiftBuildCommand.main() + await SwiftBuildCommand.main() case "swift-experimental-sdk": await SwiftSDKCommand.main() case "swift-test": SwiftTestCommand.main() case "swift-run": - SwiftRunCommand.main() + await SwiftRunCommand.main() case "swift-package-collection": await PackageCollectionsCommand.main() case "swift-package-registry": @@ -41,5 +53,7 @@ struct SwiftPM { default: fatalError("swift-package-manager launched with unexpected name: \(execName ?? "(unknown)")") } + + return true } } diff --git a/Sources/swift-run/CMakeLists.txt b/Sources/swift-run/CMakeLists.txt index 26b4ac9e321..9c609f84ea1 100644 --- a/Sources/swift-run/CMakeLists.txt +++ b/Sources/swift-run/CMakeLists.txt @@ -7,9 +7,12 @@ # See http://swift.org/CONTRIBUTORS.txt for Swift project authors add_executable(swift-run - main.swift) + Entrypoint.swift) target_link_libraries(swift-run PRIVATE Commands) +target_compile_options(swift-run PRIVATE + -parse-as-library) + install(TARGETS swift-run RUNTIME DESTINATION bin) diff --git a/Sources/swift-build/main.swift b/Sources/swift-run/Entrypoint.swift similarity index 83% rename from Sources/swift-build/main.swift rename to Sources/swift-run/Entrypoint.swift index 9600d0998cf..4976cccdb15 100644 --- a/Sources/swift-build/main.swift +++ b/Sources/swift-run/Entrypoint.swift @@ -12,4 +12,9 @@ import Commands -SwiftBuildCommand.main() +@main +struct Entrypoint { + static func main() async { + await SwiftRunCommand.main() + } +} diff --git a/Tests/CommandsTests/RunCommandTests.swift b/Tests/CommandsTests/RunCommandTests.swift index 7661902cbff..ec50b40b3ea 100644 --- a/Tests/CommandsTests/RunCommandTests.swift +++ b/Tests/CommandsTests/RunCommandTests.swift @@ -16,6 +16,7 @@ import SPMTestSupport import XCTest import class TSCBasic.Process +import enum TSCBasic.ProcessEnv final class RunCommandTests: CommandsTestCase { private func execute( @@ -121,8 +122,12 @@ final class RunCommandTests: CommandsTestCase { let sync = DispatchGroup() let outputHandler = OutputHandler(sync: sync) + + var environmentBlock = ProcessEnv.block + environmentBlock["SWIFTPM_EXEC_NAME"] = "swift-run" let process = Process( arguments: [SwiftPM.Run.xctestBinaryPath.pathString, "--package-path", fixturePath.pathString], + environmentBlock: environmentBlock, outputRedirection: .stream(stdout: outputHandler.handle(bytes:), stderr: outputHandler.handle(bytes:)) ) From 57d4e04f1666cfc398cb13be13a0a191ac90b597 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Mon, 26 Feb 2024 16:46:04 +0000 Subject: [PATCH 028/159] Benchmarks: add `PackageGraphBenchmarks` target (#7283) This adds a dependency on the [`package-benchmark`](https://github.com/ordo-one/package-benchmark) library, isolated to the new `Benchmarks` folder. Thus it doesn't touch CMake or self-hosted builds, and one can only pull the library if they run `swift package benchmark` in the `Benchmarks` directory. A single benchmark is added that loads SwiftPM's `Package.swift` and builds a package graph for it. --- .../PackageGraphBenchmarks.swift | 22 +++++++++++ Benchmarks/Package.swift | 38 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 Benchmarks/Benchmarks/PackageGraphBenchmarks/PackageGraphBenchmarks.swift create mode 100644 Benchmarks/Package.swift diff --git a/Benchmarks/Benchmarks/PackageGraphBenchmarks/PackageGraphBenchmarks.swift b/Benchmarks/Benchmarks/PackageGraphBenchmarks/PackageGraphBenchmarks.swift new file mode 100644 index 00000000000..b35bd27bb9d --- /dev/null +++ b/Benchmarks/Benchmarks/PackageGraphBenchmarks/PackageGraphBenchmarks.swift @@ -0,0 +1,22 @@ +import Basics +import Benchmark +import PackageModel +import Workspace + +let benchmarks = { + Benchmark( + "Package graph loading", + configuration: .init( + metrics: BenchmarkMetric.all, + maxDuration: .seconds(10) + ) + ) { benchmark in + let path = try AbsolutePath(validating: #file).parentDirectory.parentDirectory.parentDirectory + let workspace = try Workspace(fileSystem: localFileSystem, location: .init(forRootPackage: path, fileSystem: localFileSystem)) + let system = ObservabilitySystem { _, _ in } + + for _ in benchmark.scaledIterations { + try workspace.loadPackageGraph(rootPath: path, observabilityScope: system.topScope) + } + } +} diff --git a/Benchmarks/Package.swift b/Benchmarks/Package.swift new file mode 100644 index 00000000000..da9f26a8799 --- /dev/null +++ b/Benchmarks/Package.swift @@ -0,0 +1,38 @@ +// swift-tools-version: 5.7 +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import PackageDescription + +let package = Package( + name: "SwiftPMBenchmarks", + platforms: [ + .macOS("14"), + ], + dependencies: [ + .package(path: "../"), + .package(url: "https://github.com/ordo-one/package-benchmark.git", from: "1.13.0"), + ], + targets: [ + .executableTarget( + name: "PackageGraphBenchmarks", + dependencies: [ + .product(name: "Benchmark", package: "package-benchmark"), + .product(name: "SwiftPMDataModel", package: "SwiftPM"), + ], + path: "Benchmarks/PackageGraphBenchmarks", + plugins: [ + .plugin(name: "BenchmarkPlugin", package: "package-benchmark"), + ] + ), + ] +) From 4e8078852b9fcbc491cfc8fde442a77e7bdd73c0 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Mon, 26 Feb 2024 23:42:19 +0000 Subject: [PATCH 029/159] Revert "Revert "Start using `package` for some APIs"" (#7344) Reverts apple/swift-package-manager#7249 --- Package.swift | 2 +- Sources/Basics/CMakeLists.txt | 1 + Sources/Basics/OSSignpost.swift | 11 +++--- .../GitRepositoryExtensions.swift | 2 +- Sources/SPMTestSupport/MockArchiver.swift | 34 +++++++++---------- 5 files changed, 26 insertions(+), 24 deletions(-) diff --git a/Package.swift b/Package.swift index 8ecc9074dc0..f0e7d652a79 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.8 +// swift-tools-version:5.9 //===----------------------------------------------------------------------===// // diff --git a/Sources/Basics/CMakeLists.txt b/Sources/Basics/CMakeLists.txt index eec426c2fe9..bed9dcebfd2 100644 --- a/Sources/Basics/CMakeLists.txt +++ b/Sources/Basics/CMakeLists.txt @@ -76,6 +76,7 @@ target_link_libraries(Basics PUBLIC target_link_libraries(Basics PRIVATE SPMSQLite3 TSCclibc) + # NOTE(compnerd) workaround for CMake not setting up include flags yet set_target_properties(Basics PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY}) diff --git a/Sources/Basics/OSSignpost.swift b/Sources/Basics/OSSignpost.swift index 34eb9d49c04..96f524f2a60 100644 --- a/Sources/Basics/OSSignpost.swift +++ b/Sources/Basics/OSSignpost.swift @@ -22,7 +22,7 @@ extension os.OSLog { #endif /// Emits a signpost. -@inlinable public func os_signpost( +@inlinable package func os_signpost( _ type: SignpostType, name: StaticString, signpostID: SignpostID = .exclusive @@ -39,8 +39,8 @@ extension os.OSLog { #endif } - -public enum SignpostType { +@usableFromInline +package enum SignpostType { case begin case end case event @@ -61,7 +61,8 @@ public enum SignpostType { #endif } -public enum SignpostID { +@usableFromInline +package enum SignpostID { case exclusive #if canImport(os) @@ -77,7 +78,7 @@ public enum SignpostID { } -public enum SignpostName { +package enum SignpostName { public static let updatingDependencies: StaticString = "updating" public static let resolvingDependencies: StaticString = "resolving" public static let pubgrub: StaticString = "pubgrub" diff --git a/Sources/SPMTestSupport/GitRepositoryExtensions.swift b/Sources/SPMTestSupport/GitRepositoryExtensions.swift index 9f3c4db0d9c..5a61fd9925b 100644 --- a/Sources/SPMTestSupport/GitRepositoryExtensions.swift +++ b/Sources/SPMTestSupport/GitRepositoryExtensions.swift @@ -18,7 +18,7 @@ import enum TSCUtility.Git /// Extensions useful for unit testing purposes. /// Note: These are not thread safe. -public extension GitRepository { +package extension GitRepository { /// Create the repository using git init. func create() throws { try systemQuietly([Git.tool, "-C", self.path.pathString, "init"]) diff --git a/Sources/SPMTestSupport/MockArchiver.swift b/Sources/SPMTestSupport/MockArchiver.swift index 2bfc63546a3..cf1a84434df 100644 --- a/Sources/SPMTestSupport/MockArchiver.swift +++ b/Sources/SPMTestSupport/MockArchiver.swift @@ -12,22 +12,22 @@ import Basics -public class MockArchiver: Archiver { - public typealias ExtractionHandler = ( +package class MockArchiver: Archiver { + package typealias ExtractionHandler = ( MockArchiver, AbsolutePath, AbsolutePath, (Result) -> Void ) throws -> Void - public typealias CompressionHandler = ( + package typealias CompressionHandler = ( MockArchiver, AbsolutePath, AbsolutePath, (Result) -> Void ) throws -> Void - public typealias ValidationHandler = (MockArchiver, AbsolutePath, (Result) -> Void) throws -> Void + package typealias ValidationHandler = (MockArchiver, AbsolutePath, (Result) -> Void) throws -> Void - public struct Extraction: Equatable { + package struct Extraction: Equatable { public let archivePath: AbsolutePath public let destinationPath: AbsolutePath @@ -37,7 +37,7 @@ public class MockArchiver: Archiver { } } - public struct Compression: Equatable { + package struct Compression: Equatable { public let directory: AbsolutePath public let destinationPath: AbsolutePath @@ -47,18 +47,18 @@ public class MockArchiver: Archiver { } } - public let supportedExtensions: Set = ["zip"] - public let extractions = ThreadSafeArrayStore() - public let compressions = ThreadSafeArrayStore() - public let extractionHandler: ExtractionHandler? - public let compressionHandler: CompressionHandler? - public let validationHandler: ValidationHandler? + package let supportedExtensions: Set = ["zip"] + package let extractions = ThreadSafeArrayStore() + package let compressions = ThreadSafeArrayStore() + package let extractionHandler: ExtractionHandler? + package let compressionHandler: CompressionHandler? + package let validationHandler: ValidationHandler? - public convenience init(handler: ExtractionHandler? = .none) { + package convenience init(handler: ExtractionHandler? = .none) { self.init(extractionHandler: handler, compressionHandler: .none, validationHandler: .none) } - public init( + package init( extractionHandler: ExtractionHandler? = .none, compressionHandler: CompressionHandler? = .none, validationHandler: ValidationHandler? = .none @@ -68,7 +68,7 @@ public class MockArchiver: Archiver { self.validationHandler = validationHandler } - public func extract( + package func extract( from archivePath: AbsolutePath, to destinationPath: AbsolutePath, completion: @escaping (Result) -> Void @@ -85,7 +85,7 @@ public class MockArchiver: Archiver { } } - public func compress( + package func compress( directory: AbsolutePath, to destinationPath: AbsolutePath, completion: @escaping (Result) -> Void @@ -102,7 +102,7 @@ public class MockArchiver: Archiver { } } - public func validate(path: AbsolutePath, completion: @escaping (Result) -> Void) { + package func validate(path: AbsolutePath, completion: @escaping (Result) -> Void) { do { if let handler = self.validationHandler { try handler(self, path, completion) From 260624a7247934216cd77839937e3a1f40483ec8 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 27 Feb 2024 07:45:39 +0000 Subject: [PATCH 030/159] Clean up `main` function signature in `SwiftPM.swift` (#7370) This function always returns `true` result right now, which is discarded. We simply shouldn't return anything at all. --- Sources/swift-package-manager/SwiftPM.swift | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Sources/swift-package-manager/SwiftPM.swift b/Sources/swift-package-manager/SwiftPM.swift index 3e3d7a106ee..213c5264ccb 100644 --- a/Sources/swift-package-manager/SwiftPM.swift +++ b/Sources/swift-package-manager/SwiftPM.swift @@ -33,8 +33,7 @@ struct SwiftPM { } } - @discardableResult - private static func main(execName: String?) async -> Bool { + private static func main(execName: String?) async { switch execName { case "swift-package": await SwiftPackageCommand.main() @@ -53,7 +52,5 @@ struct SwiftPM { default: fatalError("swift-package-manager launched with unexpected name: \(execName ?? "(unknown)")") } - - return true } } From 03fef478de2d872f2b8917102f88d74a8c46c4fe Mon Sep 17 00:00:00 2001 From: Ben Barham Date: Tue, 27 Feb 2024 01:44:26 -0800 Subject: [PATCH 031/159] Revert "Exclude `XCBuildSupport` when building with CMake (#7358)" (#7371) This reverts commit 52870620e7b550b492086ae877d6eb9afd94c33f, which causes macOS toolchain builds to fail with: ``` main/main.swift:345: Fatal error: SwiftPM was built without XCBuild support ``` Also adds the DriverSupport dependency to XCBuildSupport, which was skipped in #7258 because #7358 was merged (but we're reverting that). --- Sources/CMakeLists.txt | 1 + Sources/Commands/CMakeLists.txt | 7 ++---- .../PackageCommands/DumpCommands.swift | 7 ------ .../PackageCommands/SwiftPackageCommand.swift | 3 --- Sources/Commands/SwiftBuildCommand.swift | 3 --- Sources/CoreCommands/BuildSystemSupport.swift | 11 -------- Sources/CoreCommands/CMakeLists.txt | 7 ++---- Sources/XCBuildSupport/CMakeLists.txt | 25 +++++++++++++++++++ Sources/swift-bootstrap/CMakeLists.txt | 6 ++--- Sources/swift-bootstrap/main.swift | 7 ------ 10 files changed, 32 insertions(+), 45 deletions(-) create mode 100644 Sources/XCBuildSupport/CMakeLists.txt diff --git a/Sources/CMakeLists.txt b/Sources/CMakeLists.txt index 82562eb7a01..5af06570360 100644 --- a/Sources/CMakeLists.txt +++ b/Sources/CMakeLists.txt @@ -37,3 +37,4 @@ add_subdirectory(swift-run) add_subdirectory(swift-test) add_subdirectory(SwiftSDKCommand) add_subdirectory(Workspace) +add_subdirectory(XCBuildSupport) diff --git a/Sources/Commands/CMakeLists.txt b/Sources/Commands/CMakeLists.txt index 3f2275fbe02..e74414c60bf 100644 --- a/Sources/Commands/CMakeLists.txt +++ b/Sources/Commands/CMakeLists.txt @@ -59,11 +59,8 @@ target_link_libraries(Commands PUBLIC SourceControl TSCBasic TSCUtility - Workspace) - -target_compile_definitions(Commands - PRIVATE DISABLE_XCBUILD_SUPPORT) - + Workspace + XCBuildSupport) target_link_libraries(Commands PRIVATE DriverSupport $<$>:FoundationXML>) diff --git a/Sources/Commands/PackageCommands/DumpCommands.swift b/Sources/Commands/PackageCommands/DumpCommands.swift index bde6b0d0fe1..8727d1dd0e5 100644 --- a/Sources/Commands/PackageCommands/DumpCommands.swift +++ b/Sources/Commands/PackageCommands/DumpCommands.swift @@ -15,10 +15,7 @@ import Basics import CoreCommands import Foundation import PackageModel - -#if !DISABLE_XCBUILD_SUPPORT import XCBuildSupport -#endif struct DumpSymbolGraph: SwiftCommand { static let configuration = CommandConfiguration( @@ -139,7 +136,6 @@ struct DumpPIF: SwiftCommand { var preserveStructure: Bool = false func run(_ swiftCommandState: SwiftCommandState) throws { - #if !DISABLE_XCBUILD_SUPPORT let graph = try swiftCommandState.loadPackageGraph() let pif = try PIFBuilder.generatePIF( buildParameters: swiftCommandState.productsBuildParameters, @@ -148,9 +144,6 @@ struct DumpPIF: SwiftCommand { observabilityScope: swiftCommandState.observabilityScope, preservePIFModelStructure: preserveStructure) print(pif) - #else - fatalError("This subcommand is not available on the current platform") - #endif } var toolWorkspaceConfiguration: ToolWorkspaceConfiguration { diff --git a/Sources/Commands/PackageCommands/SwiftPackageCommand.swift b/Sources/Commands/PackageCommands/SwiftPackageCommand.swift index 826e22912b0..8c2170e2f1f 100644 --- a/Sources/Commands/PackageCommands/SwiftPackageCommand.swift +++ b/Sources/Commands/PackageCommands/SwiftPackageCommand.swift @@ -20,10 +20,7 @@ import PackageModel import SourceControl import SPMBuildCore import Workspace - -#if !DISABLE_XCBUILD_SUPPORT import XCBuildSupport -#endif import enum TSCUtility.Diagnostics diff --git a/Sources/Commands/SwiftBuildCommand.swift b/Sources/Commands/SwiftBuildCommand.swift index a29eca9d0e1..96b9a813d23 100644 --- a/Sources/Commands/SwiftBuildCommand.swift +++ b/Sources/Commands/SwiftBuildCommand.swift @@ -16,10 +16,7 @@ import Build import CoreCommands import PackageGraph import SPMBuildCore - -#if !DISABLE_XCBUILD_SUPPORT import XCBuildSupport -#endif import class TSCBasic.Process import var TSCBasic.stdoutStream diff --git a/Sources/CoreCommands/BuildSystemSupport.swift b/Sources/CoreCommands/BuildSystemSupport.swift index 567b2f0d186..3df40912808 100644 --- a/Sources/CoreCommands/BuildSystemSupport.swift +++ b/Sources/CoreCommands/BuildSystemSupport.swift @@ -12,10 +12,7 @@ import Build import SPMBuildCore - -#if !DISABLE_XCBUILD_SUPPORT import XCBuildSupport -#endif import class Basics.ObservabilityScope import struct PackageGraph.ModulesGraph @@ -63,7 +60,6 @@ private struct NativeBuildSystemFactory: BuildSystemFactory { } } -#if !DISABLE_XCBUILD_SUPPORT private struct XcodeBuildSystemFactory: BuildSystemFactory { let swiftCommandState: SwiftCommandState @@ -91,19 +87,12 @@ private struct XcodeBuildSystemFactory: BuildSystemFactory { ) } } -#endif extension SwiftCommandState { public var defaultBuildSystemProvider: BuildSystemProvider { - #if !DISABLE_XCBUILD_SUPPORT .init(providers: [ .native: NativeBuildSystemFactory(swiftCommandState: self), .xcode: XcodeBuildSystemFactory(swiftCommandState: self) ]) - #else - .init(providers: [ - .native: NativeBuildSystemFactory(swiftCommandState: self), - ]) - #endif } } diff --git a/Sources/CoreCommands/CMakeLists.txt b/Sources/CoreCommands/CMakeLists.txt index f9ae401890a..d7ec0cbb632 100644 --- a/Sources/CoreCommands/CMakeLists.txt +++ b/Sources/CoreCommands/CMakeLists.txt @@ -18,11 +18,8 @@ target_link_libraries(CoreCommands PUBLIC PackageGraph TSCBasic TSCUtility - Workspace) - -target_compile_definitions(CoreCommands - PRIVATE DISABLE_XCBUILD_SUPPORT) - + Workspace + XCBuildSupport) target_link_libraries(CoreCommands PRIVATE DriverSupport $<$>:FoundationXML>) diff --git a/Sources/XCBuildSupport/CMakeLists.txt b/Sources/XCBuildSupport/CMakeLists.txt new file mode 100644 index 00000000000..b478bdb56bd --- /dev/null +++ b/Sources/XCBuildSupport/CMakeLists.txt @@ -0,0 +1,25 @@ +# This source file is part of the Swift open source project +# +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See http://swift.org/LICENSE.txt for license information +# See http://swift.org/CONTRIBUTORS.txt for Swift project authors + +add_library(XCBuildSupport STATIC + PIF.swift + PIFBuilder.swift + XCBuildDelegate.swift + XCBuildMessage.swift + XCBuildOutputParser.swift + XcodeBuildSystem.swift) +target_link_libraries(XCBuildSupport PUBLIC + Build + DriverSupport + TSCBasic + TSCUtility + PackageGraph +) + +set_target_properties(XCBuildSupport PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY}) diff --git a/Sources/swift-bootstrap/CMakeLists.txt b/Sources/swift-bootstrap/CMakeLists.txt index e8cb9c4e879..92052ac7098 100644 --- a/Sources/swift-bootstrap/CMakeLists.txt +++ b/Sources/swift-bootstrap/CMakeLists.txt @@ -17,7 +17,5 @@ target_link_libraries(swift-bootstrap PRIVATE PackageModel SwiftDriver TSCBasic - TSCUtility) - -target_compile_definitions(swift-bootstrap - PRIVATE DISABLE_XCBUILD_SUPPORT) + TSCUtility + XCBuildSupport) diff --git a/Sources/swift-bootstrap/main.swift b/Sources/swift-bootstrap/main.swift index 55f6b58e000..03d505d269d 100644 --- a/Sources/swift-bootstrap/main.swift +++ b/Sources/swift-bootstrap/main.swift @@ -24,10 +24,7 @@ import PackageGraph import PackageLoading import PackageModel import SPMBuildCore - -#if !DISABLE_XCBUILD_SUPPORT import XCBuildSupport -#endif import struct TSCBasic.KeyedPair import func TSCBasic.topologicalSort @@ -332,7 +329,6 @@ struct SwiftBootstrapBuildTool: ParsableCommand { observabilityScope: self.observabilityScope ) case .xcode: - #if !DISABLE_XCBUILD_SUPPORT return try XcodeBuildSystem( buildParameters: buildParameters, packageGraphLoader: packageGraphLoader, @@ -341,9 +337,6 @@ struct SwiftBootstrapBuildTool: ParsableCommand { fileSystem: self.fileSystem, observabilityScope: self.observabilityScope ) - #else - fatalError("SwiftPM was built without XCBuild support") - #endif } } From 92d65bf2a4aeb32c7cd73abf3b4cbe3130a46c27 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 27 Feb 2024 13:20:49 +0000 Subject: [PATCH 032/159] Hide build system internals with `@_spi` (#7365) These details should not leak outside of the SwiftPM package. In the (hopefully temporary) absence of `package` access control, we can hide build system-related APIs with `@_spi` annotations. --- Sources/Build/BuildOperation.swift | 8 ++++++++ .../BuildOperationBuildSystemDelegateHandler.swift | 3 +++ Sources/Commands/CommandWorkspaceDelegate.swift | 3 +++ Sources/Commands/PackageCommands/APIDiff.swift | 6 ++++++ Sources/Commands/PackageCommands/ArchiveSource.swift | 3 +++ .../Commands/PackageCommands/CompletionCommand.swift | 2 ++ .../Commands/PackageCommands/ComputeChecksum.swift | 3 +++ Sources/Commands/PackageCommands/Config.swift | 3 +++ Sources/Commands/PackageCommands/Describe.swift | 3 +++ Sources/Commands/PackageCommands/DumpCommands.swift | 7 +++++++ Sources/Commands/PackageCommands/EditCommands.swift | 3 +++ Sources/Commands/PackageCommands/Format.swift | 3 +++ Sources/Commands/PackageCommands/Init.swift | 3 +++ .../Commands/PackageCommands/InstalledPackages.swift | 7 +++++++ Sources/Commands/PackageCommands/Learn.swift | 3 +++ Sources/Commands/PackageCommands/PluginCommand.swift | 6 ++++++ Sources/Commands/PackageCommands/ResetCommands.swift | 2 ++ Sources/Commands/PackageCommands/Resolve.swift | 3 +++ .../Commands/PackageCommands/ShowDependencies.swift | 3 +++ .../PackageCommands/SwiftPackageCommand.swift | 3 +++ .../PackageCommands/ToolsVersionCommand.swift | 3 +++ Sources/Commands/PackageCommands/Update.swift | 3 +++ Sources/Commands/Snippets/CardStack.swift | 3 +++ Sources/Commands/Snippets/Cards/SnippetCard.swift | 7 +++++++ .../Commands/Snippets/Cards/SnippetGroupCard.swift | 2 ++ Sources/Commands/Snippets/Cards/TopCard.swift | 3 +++ Sources/Commands/SwiftBuildCommand.swift | 8 ++++++++ Sources/Commands/SwiftRunCommand.swift | 7 +++++++ Sources/Commands/SwiftTestCommand.swift | 8 ++++++++ Sources/Commands/Utilities/APIDigester.swift | 5 +++++ Sources/Commands/Utilities/PluginDelegate.swift | 5 +++++ Sources/Commands/Utilities/TestingSupport.swift | 3 +++ Sources/CoreCommands/BuildSystemSupport.swift | 6 ++++++ Sources/CoreCommands/Options.swift | 3 +++ Sources/CoreCommands/SwiftCommandState.swift | 8 ++++++++ .../PackageCollectionsCommand.swift | 5 +++++ .../PackageRegistryCommand+Auth.swift | 5 +++++ .../PackageRegistryCommand+Publish.swift | 5 +++++ .../PackageRegistryCommand.swift | 6 ++++++ Sources/SPMBuildCore/BuildSystem/BuildSystem.swift | 4 ++++ .../BuildSystem/BuildSystemCommand.swift | 1 + .../BuildSystem/BuildSystemDelegate.swift | 3 ++- Sources/SPMBuildCore/Triple+Extensions.swift | 1 + Sources/SPMTestSupport/misc.swift | 2 +- Sources/XCBuildSupport/XCBuildDelegate.swift | 3 +++ Sources/XCBuildSupport/XcodeBuildSystem.swift | 1 + Sources/swift-bootstrap/main.swift | 7 +++++++ Sources/swift-build/Entrypoint.swift | 1 + Sources/swift-package-manager/SwiftPM.swift | 3 +++ Sources/swift-run/Entrypoint.swift | 1 + Sources/swift-test/main.swift | 1 + Tests/BuildTests/BuildOperationTests.swift | 12 ++++++++++-- Tests/CommandsTests/BuildCommandTests.swift | 6 +++++- .../MermaidPackageSerializerTests.swift | 8 ++++---- Tests/CommandsTests/PackageCommandTests.swift | 12 +++++++++--- Tests/CommandsTests/SwiftCommandStateTests.swift | 8 ++++++-- 56 files changed, 232 insertions(+), 14 deletions(-) diff --git a/Sources/Build/BuildOperation.swift b/Sources/Build/BuildOperation.swift index 3d92d1676c2..ca959a84d18 100644 --- a/Sources/Build/BuildOperation.swift +++ b/Sources/Build/BuildOperation.swift @@ -12,11 +12,18 @@ @_spi(SwiftPMInternal) import Basics + +@_spi(SwiftPMInternal) +import Build + import LLBuildManifest import PackageGraph import PackageLoading import PackageModel + +@_spi(SwiftPMInternal) import SPMBuildCore + import SPMLLBuild import Foundation @@ -36,6 +43,7 @@ import DriverSupport import SwiftDriver #endif +@_spi(SwiftPMInternal) public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildSystem, BuildErrorAdviceProvider { /// The delegate used by the build system. public weak var delegate: SPMBuildCore.BuildSystemDelegate? diff --git a/Sources/Build/BuildOperationBuildSystemDelegateHandler.swift b/Sources/Build/BuildOperationBuildSystemDelegateHandler.swift index 77d1b3cf5b8..e9814f2e917 100644 --- a/Sources/Build/BuildOperationBuildSystemDelegateHandler.swift +++ b/Sources/Build/BuildOperationBuildSystemDelegateHandler.swift @@ -16,7 +16,10 @@ import Dispatch import Foundation import LLBuildManifest import PackageModel + +@_spi(SwiftPMInternal) import SPMBuildCore + import SPMLLBuild import struct TSCBasic.ByteString diff --git a/Sources/Commands/CommandWorkspaceDelegate.swift b/Sources/Commands/CommandWorkspaceDelegate.swift index accec9fe9a3..e5e5983fb0e 100644 --- a/Sources/Commands/CommandWorkspaceDelegate.swift +++ b/Sources/Commands/CommandWorkspaceDelegate.swift @@ -11,7 +11,10 @@ //===----------------------------------------------------------------------===// import Basics + +@_spi(SwiftPMInternal) import CoreCommands + import Dispatch import class Foundation.NSLock import struct Foundation.URL diff --git a/Sources/Commands/PackageCommands/APIDiff.swift b/Sources/Commands/PackageCommands/APIDiff.swift index acb38e87ed0..8606732616d 100644 --- a/Sources/Commands/PackageCommands/APIDiff.swift +++ b/Sources/Commands/PackageCommands/APIDiff.swift @@ -12,12 +12,18 @@ import ArgumentParser import Basics + +@_spi(SwiftPMInternal) import CoreCommands + import Dispatch import PackageGraph import PackageModel import SourceControl +@_spi(SwiftPMInternal) +import SPMBuildCore + struct DeprecatedAPIDiff: ParsableCommand { static let configuration = CommandConfiguration(commandName: "experimental-api-diff", abstract: "Deprecated - use `swift package diagnose-api-breaking-changes` instead", diff --git a/Sources/Commands/PackageCommands/ArchiveSource.swift b/Sources/Commands/PackageCommands/ArchiveSource.swift index 01d2b7695ef..9dd95058c0a 100644 --- a/Sources/Commands/PackageCommands/ArchiveSource.swift +++ b/Sources/Commands/PackageCommands/ArchiveSource.swift @@ -12,7 +12,10 @@ import ArgumentParser import Basics + +@_spi(SwiftPMInternal) import CoreCommands + import SourceControl extension SwiftPackageCommand { diff --git a/Sources/Commands/PackageCommands/CompletionCommand.swift b/Sources/Commands/PackageCommands/CompletionCommand.swift index 1326d005383..6eadce8a34f 100644 --- a/Sources/Commands/PackageCommands/CompletionCommand.swift +++ b/Sources/Commands/PackageCommands/CompletionCommand.swift @@ -11,6 +11,8 @@ //===----------------------------------------------------------------------===// import ArgumentParser + +@_spi(SwiftPMInternal) import CoreCommands import var TSCBasic.stdoutStream diff --git a/Sources/Commands/PackageCommands/ComputeChecksum.swift b/Sources/Commands/PackageCommands/ComputeChecksum.swift index 9a0b92e78e0..73ca9541d97 100644 --- a/Sources/Commands/PackageCommands/ComputeChecksum.swift +++ b/Sources/Commands/PackageCommands/ComputeChecksum.swift @@ -12,7 +12,10 @@ import ArgumentParser import Basics + +@_spi(SwiftPMInternal) import CoreCommands + import Workspace import struct TSCBasic.SHA256 diff --git a/Sources/Commands/PackageCommands/Config.swift b/Sources/Commands/PackageCommands/Config.swift index 0d61c59a449..83eb1d4d91d 100644 --- a/Sources/Commands/PackageCommands/Config.swift +++ b/Sources/Commands/PackageCommands/Config.swift @@ -12,7 +12,10 @@ import ArgumentParser import Basics + +@_spi(SwiftPMInternal) import CoreCommands + import Workspace import var TSCBasic.stderrStream diff --git a/Sources/Commands/PackageCommands/Describe.swift b/Sources/Commands/PackageCommands/Describe.swift index ef8001c6e7e..d8d283fbb7a 100644 --- a/Sources/Commands/PackageCommands/Describe.swift +++ b/Sources/Commands/PackageCommands/Describe.swift @@ -12,7 +12,10 @@ import ArgumentParser import Basics + +@_spi(SwiftPMInternal) import CoreCommands + import Foundation import PackageModel diff --git a/Sources/Commands/PackageCommands/DumpCommands.swift b/Sources/Commands/PackageCommands/DumpCommands.swift index 8727d1dd0e5..b8f660ba73b 100644 --- a/Sources/Commands/PackageCommands/DumpCommands.swift +++ b/Sources/Commands/PackageCommands/DumpCommands.swift @@ -12,9 +12,16 @@ import ArgumentParser import Basics + +@_spi(SwiftPMInternal) import CoreCommands + import Foundation import PackageModel + +@_spi(SwiftPMInternal) +import SPMBuildCore + import XCBuildSupport struct DumpSymbolGraph: SwiftCommand { diff --git a/Sources/Commands/PackageCommands/EditCommands.swift b/Sources/Commands/PackageCommands/EditCommands.swift index 42195f7037d..a86d3889467 100644 --- a/Sources/Commands/PackageCommands/EditCommands.swift +++ b/Sources/Commands/PackageCommands/EditCommands.swift @@ -12,7 +12,10 @@ import ArgumentParser import Basics + +@_spi(SwiftPMInternal) import CoreCommands + import SourceControl extension SwiftPackageCommand { diff --git a/Sources/Commands/PackageCommands/Format.swift b/Sources/Commands/PackageCommands/Format.swift index f4bfdce769a..dc577594418 100644 --- a/Sources/Commands/PackageCommands/Format.swift +++ b/Sources/Commands/PackageCommands/Format.swift @@ -12,7 +12,10 @@ import ArgumentParser import Basics + +@_spi(SwiftPMInternal) import CoreCommands + import PackageModel import class TSCBasic.Process diff --git a/Sources/Commands/PackageCommands/Init.swift b/Sources/Commands/PackageCommands/Init.swift index a8555b00f0a..847af06b803 100644 --- a/Sources/Commands/PackageCommands/Init.swift +++ b/Sources/Commands/PackageCommands/Init.swift @@ -12,7 +12,10 @@ import ArgumentParser import Basics + +@_spi(SwiftPMInternal) import CoreCommands + import Workspace import SPMBuildCore diff --git a/Sources/Commands/PackageCommands/InstalledPackages.swift b/Sources/Commands/PackageCommands/InstalledPackages.swift index 0ad9603eb7b..0e304e788a8 100644 --- a/Sources/Commands/PackageCommands/InstalledPackages.swift +++ b/Sources/Commands/PackageCommands/InstalledPackages.swift @@ -11,9 +11,16 @@ //===----------------------------------------------------------------------===// import ArgumentParser + +@_spi(SwiftPMInternal) import CoreCommands + import Foundation import PackageModel + +@_spi(SwiftPMInternal) +import SPMBuildCore + import TSCBasic extension SwiftPackageCommand { diff --git a/Sources/Commands/PackageCommands/Learn.swift b/Sources/Commands/PackageCommands/Learn.swift index 3632c297d6e..df6bbc758e6 100644 --- a/Sources/Commands/PackageCommands/Learn.swift +++ b/Sources/Commands/PackageCommands/Learn.swift @@ -12,7 +12,10 @@ import ArgumentParser import Basics + +@_spi(SwiftPMInternal) import CoreCommands + import PackageGraph import PackageModel diff --git a/Sources/Commands/PackageCommands/PluginCommand.swift b/Sources/Commands/PackageCommands/PluginCommand.swift index 2fac7c1498d..0c472b05efb 100644 --- a/Sources/Commands/PackageCommands/PluginCommand.swift +++ b/Sources/Commands/PackageCommands/PluginCommand.swift @@ -12,7 +12,13 @@ import ArgumentParser import Basics + +@_spi(SwiftPMInternal) import CoreCommands + +@_spi(SwiftPMInternal) +import SPMBuildCore + import Dispatch import PackageGraph import PackageModel diff --git a/Sources/Commands/PackageCommands/ResetCommands.swift b/Sources/Commands/PackageCommands/ResetCommands.swift index fd2a872c228..81b07745524 100644 --- a/Sources/Commands/PackageCommands/ResetCommands.swift +++ b/Sources/Commands/PackageCommands/ResetCommands.swift @@ -11,6 +11,8 @@ //===----------------------------------------------------------------------===// import ArgumentParser + +@_spi(SwiftPMInternal) import CoreCommands extension SwiftPackageCommand { diff --git a/Sources/Commands/PackageCommands/Resolve.swift b/Sources/Commands/PackageCommands/Resolve.swift index 2107e60bc67..bae46153e9f 100644 --- a/Sources/Commands/PackageCommands/Resolve.swift +++ b/Sources/Commands/PackageCommands/Resolve.swift @@ -11,7 +11,10 @@ //===----------------------------------------------------------------------===// import ArgumentParser + +@_spi(SwiftPMInternal) import CoreCommands + import TSCUtility extension SwiftPackageCommand { diff --git a/Sources/Commands/PackageCommands/ShowDependencies.swift b/Sources/Commands/PackageCommands/ShowDependencies.swift index 1572c6e0f63..086b0815b4e 100644 --- a/Sources/Commands/PackageCommands/ShowDependencies.swift +++ b/Sources/Commands/PackageCommands/ShowDependencies.swift @@ -13,7 +13,10 @@ import ArgumentParser import Basics + +@_spi(SwiftPMInternal) import CoreCommands + import PackageGraph import class TSCBasic.LocalFileOutputByteStream diff --git a/Sources/Commands/PackageCommands/SwiftPackageCommand.swift b/Sources/Commands/PackageCommands/SwiftPackageCommand.swift index 8c2170e2f1f..b8497dcbc09 100644 --- a/Sources/Commands/PackageCommands/SwiftPackageCommand.swift +++ b/Sources/Commands/PackageCommands/SwiftPackageCommand.swift @@ -12,7 +12,10 @@ import ArgumentParser import Basics + +@_spi(SwiftPMInternal) import CoreCommands + import Foundation import PackageGraph import PackageLoading diff --git a/Sources/Commands/PackageCommands/ToolsVersionCommand.swift b/Sources/Commands/PackageCommands/ToolsVersionCommand.swift index 035a47ef7f4..8c167a4a8a6 100644 --- a/Sources/Commands/PackageCommands/ToolsVersionCommand.swift +++ b/Sources/Commands/PackageCommands/ToolsVersionCommand.swift @@ -11,7 +11,10 @@ //===----------------------------------------------------------------------===// import ArgumentParser + +@_spi(SwiftPMInternal) import CoreCommands + import PackageLoading import PackageModel import Workspace diff --git a/Sources/Commands/PackageCommands/Update.swift b/Sources/Commands/PackageCommands/Update.swift index 50a325ab660..c222c469d56 100644 --- a/Sources/Commands/PackageCommands/Update.swift +++ b/Sources/Commands/PackageCommands/Update.swift @@ -11,7 +11,10 @@ //===----------------------------------------------------------------------===// import ArgumentParser + +@_spi(SwiftPMInternal) import CoreCommands + import Dispatch import PackageModel import PackageGraph diff --git a/Sources/Commands/Snippets/CardStack.swift b/Sources/Commands/Snippets/CardStack.swift index 72d9c5d0c5b..b9c02a80b77 100644 --- a/Sources/Commands/Snippets/CardStack.swift +++ b/Sources/Commands/Snippets/CardStack.swift @@ -11,7 +11,10 @@ //===----------------------------------------------------------------------===// import Basics + +@_spi(SwiftPMInternal) import CoreCommands + import PackageGraph import PackageModel diff --git a/Sources/Commands/Snippets/Cards/SnippetCard.swift b/Sources/Commands/Snippets/Cards/SnippetCard.swift index 72f1afa1017..bf87796d5f9 100644 --- a/Sources/Commands/Snippets/Cards/SnippetCard.swift +++ b/Sources/Commands/Snippets/Cards/SnippetCard.swift @@ -11,7 +11,14 @@ //===----------------------------------------------------------------------===// import Basics + +@_spi(SwiftPMInternal) import CoreCommands + + +@_spi(SwiftPMInternal) +import SPMBuildCore + import PackageModel import enum TSCBasic.ProcessEnv diff --git a/Sources/Commands/Snippets/Cards/SnippetGroupCard.swift b/Sources/Commands/Snippets/Cards/SnippetGroupCard.swift index 2636d31d586..42f21be615c 100644 --- a/Sources/Commands/Snippets/Cards/SnippetGroupCard.swift +++ b/Sources/Commands/Snippets/Cards/SnippetGroupCard.swift @@ -10,7 +10,9 @@ // //===----------------------------------------------------------------------===// +@_spi(SwiftPMInternal) import CoreCommands + import PackageModel /// A card showing the snippets in a ``SnippetGroup``. diff --git a/Sources/Commands/Snippets/Cards/TopCard.swift b/Sources/Commands/Snippets/Cards/TopCard.swift index 4b7ed38a418..89fe006d487 100644 --- a/Sources/Commands/Snippets/Cards/TopCard.swift +++ b/Sources/Commands/Snippets/Cards/TopCard.swift @@ -10,7 +10,10 @@ // //===----------------------------------------------------------------------===// + +@_spi(SwiftPMInternal) import CoreCommands + import Foundation import PackageModel import PackageGraph diff --git a/Sources/Commands/SwiftBuildCommand.swift b/Sources/Commands/SwiftBuildCommand.swift index 96b9a813d23..468f3b8d25d 100644 --- a/Sources/Commands/SwiftBuildCommand.swift +++ b/Sources/Commands/SwiftBuildCommand.swift @@ -12,9 +12,16 @@ import ArgumentParser import Basics + +@_spi(SwiftPMInternal) import Build + +@_spi(SwiftPMInternal) import CoreCommands + import PackageGraph + +@_spi(SwiftPMInternal) import SPMBuildCore import XCBuildSupport @@ -92,6 +99,7 @@ struct BuildCommandOptions: ParsableArguments { } /// swift-build command namespace +@_spi(SwiftPMInternal) public struct SwiftBuildCommand: AsyncSwiftCommand { public static var configuration = CommandConfiguration( commandName: "build", diff --git a/Sources/Commands/SwiftRunCommand.swift b/Sources/Commands/SwiftRunCommand.swift index de7e9077090..c92431cf908 100644 --- a/Sources/Commands/SwiftRunCommand.swift +++ b/Sources/Commands/SwiftRunCommand.swift @@ -12,11 +12,17 @@ import ArgumentParser import Basics + +@_spi(SwiftPMInternal) import CoreCommands + import Foundation import PackageGraph import PackageModel +@_spi(SwiftPMInternal) +import SPMBuildCore + import enum TSCBasic.ProcessEnv import func TSCBasic.exec @@ -90,6 +96,7 @@ struct RunCommandOptions: ParsableArguments { } /// swift-run command namespace +@_spi(SwiftPMInternal) public struct SwiftRunCommand: AsyncSwiftCommand { public static var configuration = CommandConfiguration( commandName: "run", diff --git a/Sources/Commands/SwiftTestCommand.swift b/Sources/Commands/SwiftTestCommand.swift index 45bdca6fd70..76c76258e70 100644 --- a/Sources/Commands/SwiftTestCommand.swift +++ b/Sources/Commands/SwiftTestCommand.swift @@ -11,14 +11,21 @@ //===----------------------------------------------------------------------===// import ArgumentParser + @_spi(SwiftPMInternal) import Basics + +@_spi(SwiftPMInternal) import CoreCommands + import Dispatch import Foundation import PackageGraph import PackageModel + +@_spi(SwiftPMInternal) import SPMBuildCore + import func TSCLibc.exit import Workspace @@ -228,6 +235,7 @@ public enum TestOutput: String, ExpressibleByArgument { } /// swift-test tool namespace +@_spi(SwiftPMInternal) public struct SwiftTestCommand: SwiftCommand { public static var configuration = CommandConfiguration( commandName: "test", diff --git a/Sources/Commands/Utilities/APIDigester.swift b/Sources/Commands/Utilities/APIDigester.swift index 2826d9cd266..e229a775718 100644 --- a/Sources/Commands/Utilities/APIDigester.swift +++ b/Sources/Commands/Utilities/APIDigester.swift @@ -13,9 +13,14 @@ import Dispatch import Foundation +@_spi(SwiftPMInternal) import SPMBuildCore + import Basics + +@_spi(SwiftPMInternal) import CoreCommands + import PackageGraph import PackageModel import SourceControl diff --git a/Sources/Commands/Utilities/PluginDelegate.swift b/Sources/Commands/Utilities/PluginDelegate.swift index 72eb0c7d387..962786756e9 100644 --- a/Sources/Commands/Utilities/PluginDelegate.swift +++ b/Sources/Commands/Utilities/PluginDelegate.swift @@ -11,9 +11,14 @@ //===----------------------------------------------------------------------===// import Basics + +@_spi(SwiftPMInternal) import CoreCommands + import Foundation import PackageModel + +@_spi(SwiftPMInternal) import SPMBuildCore import protocol TSCBasic.OutputByteStream diff --git a/Sources/Commands/Utilities/TestingSupport.swift b/Sources/Commands/Utilities/TestingSupport.swift index 335b0a9995d..9d1d15b93cf 100644 --- a/Sources/Commands/Utilities/TestingSupport.swift +++ b/Sources/Commands/Utilities/TestingSupport.swift @@ -11,7 +11,10 @@ //===----------------------------------------------------------------------===// import Basics + +@_spi(SwiftPMInternal) import CoreCommands + import PackageModel import SPMBuildCore import Workspace diff --git a/Sources/CoreCommands/BuildSystemSupport.swift b/Sources/CoreCommands/BuildSystemSupport.swift index 3df40912808..5471d924e3e 100644 --- a/Sources/CoreCommands/BuildSystemSupport.swift +++ b/Sources/CoreCommands/BuildSystemSupport.swift @@ -10,8 +10,13 @@ // //===----------------------------------------------------------------------===// +@_spi(SwiftPMInternal) import Build + +@_spi(SwiftPMInternal) import SPMBuildCore + +@_spi(SwiftPMInternal) import XCBuildSupport import class Basics.ObservabilityScope @@ -89,6 +94,7 @@ private struct XcodeBuildSystemFactory: BuildSystemFactory { } extension SwiftCommandState { + @_spi(SwiftPMInternal) public var defaultBuildSystemProvider: BuildSystemProvider { .init(providers: [ .native: NativeBuildSystemFactory(swiftCommandState: self), diff --git a/Sources/CoreCommands/Options.swift b/Sources/CoreCommands/Options.swift index 006372edfff..b44bcb1e85b 100644 --- a/Sources/CoreCommands/Options.swift +++ b/Sources/CoreCommands/Options.swift @@ -24,6 +24,7 @@ import struct PackageModel.EnabledSanitizers import struct PackageModel.PackageIdentity import enum PackageModel.Sanitizer +@_spi(SwiftPMInternal) import struct SPMBuildCore.BuildSystemProvider import struct TSCBasic.StringError @@ -32,6 +33,7 @@ import struct TSCUtility.Version import struct Workspace.WorkspaceConfiguration +@_spi(SwiftPMInternal) public struct GlobalOptions: ParsableArguments { public init() {} @@ -295,6 +297,7 @@ public struct ResolverOptions: ParsableArguments { } } +@_spi(SwiftPMInternal) public struct BuildOptions: ParsableArguments { public init() {} diff --git a/Sources/CoreCommands/SwiftCommandState.swift b/Sources/CoreCommands/SwiftCommandState.swift index 835d6100b4c..db0aa3e8a38 100644 --- a/Sources/CoreCommands/SwiftCommandState.swift +++ b/Sources/CoreCommands/SwiftCommandState.swift @@ -17,9 +17,13 @@ import class Foundation.NSLock import class Foundation.ProcessInfo import PackageGraph import PackageLoading + @_spi(SwiftPMInternal) import PackageModel + +@_spi(SwiftPMInternal) import SPMBuildCore + import Workspace #if USE_IMPL_ONLY_IMPORTS @@ -80,6 +84,7 @@ public typealias WorkspaceDelegateProvider = ( public typealias WorkspaceLoaderProvider = (_ fileSystem: FileSystem, _ observabilityScope: ObservabilityScope) -> WorkspaceLoader +@_spi(SwiftPMInternal) public protocol _SwiftCommand { var globalOptions: GlobalOptions { get } var toolWorkspaceConfiguration: ToolWorkspaceConfiguration { get } @@ -94,6 +99,7 @@ extension _SwiftCommand { } } +@_spi(SwiftPMInternal) public protocol SwiftCommand: ParsableCommand, _SwiftCommand { func run(_ swiftCommandState: SwiftCommandState) throws } @@ -134,6 +140,7 @@ extension SwiftCommand { } } +@_spi(SwiftPMInternal) public protocol AsyncSwiftCommand: AsyncParsableCommand, _SwiftCommand { func run(_ swiftCommandState: SwiftCommandState) async throws } @@ -175,6 +182,7 @@ extension AsyncSwiftCommand { } } +@_spi(SwiftPMInternal) public final class SwiftCommandState { #if os(Windows) // unfortunately this is needed for C callback handlers used by Windows shutdown handler diff --git a/Sources/PackageCollectionsCommand/PackageCollectionsCommand.swift b/Sources/PackageCollectionsCommand/PackageCollectionsCommand.swift index d12e15240fe..9af6dd3cbbd 100644 --- a/Sources/PackageCollectionsCommand/PackageCollectionsCommand.swift +++ b/Sources/PackageCollectionsCommand/PackageCollectionsCommand.swift @@ -12,8 +12,13 @@ import ArgumentParser import Basics + +@_spi(SwiftPMInternal) import Commands + +@_spi(SwiftPMInternal) import CoreCommands + import Foundation import PackageCollections import PackageModel diff --git a/Sources/PackageRegistryCommand/PackageRegistryCommand+Auth.swift b/Sources/PackageRegistryCommand/PackageRegistryCommand+Auth.swift index fb15d965cb6..01079b9dff3 100644 --- a/Sources/PackageRegistryCommand/PackageRegistryCommand+Auth.swift +++ b/Sources/PackageRegistryCommand/PackageRegistryCommand+Auth.swift @@ -12,8 +12,13 @@ import ArgumentParser import Basics + +@_spi(SwiftPMInternal) import Commands + +@_spi(SwiftPMInternal) import CoreCommands + import Foundation import PackageModel import PackageRegistry diff --git a/Sources/PackageRegistryCommand/PackageRegistryCommand+Publish.swift b/Sources/PackageRegistryCommand/PackageRegistryCommand+Publish.swift index 6e09e335d2d..2a5458c196c 100644 --- a/Sources/PackageRegistryCommand/PackageRegistryCommand+Publish.swift +++ b/Sources/PackageRegistryCommand/PackageRegistryCommand+Publish.swift @@ -12,8 +12,13 @@ import ArgumentParser import Basics + +@_spi(SwiftPMInternal) import Commands + +@_spi(SwiftPMInternal) import CoreCommands + import Foundation import PackageModel import PackageRegistry diff --git a/Sources/PackageRegistryCommand/PackageRegistryCommand.swift b/Sources/PackageRegistryCommand/PackageRegistryCommand.swift index 2861b53e336..25a8e3bb3ad 100644 --- a/Sources/PackageRegistryCommand/PackageRegistryCommand.swift +++ b/Sources/PackageRegistryCommand/PackageRegistryCommand.swift @@ -12,7 +12,13 @@ import ArgumentParser import Basics + +@_spi(SwiftPMInternal) import CoreCommands + +@_spi(SwiftPMInternal) +import Commands + import Foundation import PackageModel import PackageRegistry diff --git a/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift b/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift index 8725be2e786..14349c184bf 100644 --- a/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift +++ b/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift @@ -33,6 +33,7 @@ public enum BuildSubset { /// A protocol that represents a build system used by SwiftPM for all build operations. This allows factoring out the /// implementation details between SwiftPM's `BuildOperation` and the XCBuild backed `XCBuildSystem`. +@_spi(SwiftPMInternal) public protocol BuildSystem: Cancellable { /// The delegate used by the build system. @@ -114,6 +115,7 @@ extension BuildPlan { } } +@_spi(SwiftPMInternal) public protocol BuildSystemFactory { func makeBuildSystem( explicitProduct: String?, @@ -127,6 +129,7 @@ public protocol BuildSystemFactory { ) throws -> any BuildSystem } +@_spi(SwiftPMInternal) public struct BuildSystemProvider { // TODO: In the future, we may want this to be about specific capabilities of a build system rather than choosing a concrete one. public enum Kind: String, CaseIterable { @@ -171,6 +174,7 @@ private enum Errors: Swift.Error { case buildSystemProviderNotRegistered(kind: BuildSystemProvider.Kind) } +@_spi(SwiftPMInternal) public enum BuildSystemUtilities { /// Returns the build path from the environment, if present. public static func getEnvBuildPath(workingDir: AbsolutePath) throws -> AbsolutePath? { diff --git a/Sources/SPMBuildCore/BuildSystem/BuildSystemCommand.swift b/Sources/SPMBuildCore/BuildSystem/BuildSystemCommand.swift index 4fd1273ead4..59bdc266b4d 100644 --- a/Sources/SPMBuildCore/BuildSystem/BuildSystemCommand.swift +++ b/Sources/SPMBuildCore/BuildSystem/BuildSystemCommand.swift @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +@_spi(SwiftPMInternal) public struct BuildSystemCommand: Hashable { public let name: String public let description: String diff --git a/Sources/SPMBuildCore/BuildSystem/BuildSystemDelegate.swift b/Sources/SPMBuildCore/BuildSystem/BuildSystemDelegate.swift index 163c16f16e3..b153190ea1f 100644 --- a/Sources/SPMBuildCore/BuildSystem/BuildSystemDelegate.swift +++ b/Sources/SPMBuildCore/BuildSystem/BuildSystemDelegate.swift @@ -12,7 +12,8 @@ import Foundation -/// BuildSystem delegate +/// ``BuildSystem`` delegate +@_spi(SwiftPMInternal) public protocol BuildSystemDelegate: AnyObject { ///Called when build command is about to start. func buildSystem(_ buildSystem: BuildSystem, willStartCommand command: BuildSystemCommand) diff --git a/Sources/SPMBuildCore/Triple+Extensions.swift b/Sources/SPMBuildCore/Triple+Extensions.swift index bf9a1216c3d..cdaa1f4cd5c 100644 --- a/Sources/SPMBuildCore/Triple+Extensions.swift +++ b/Sources/SPMBuildCore/Triple+Extensions.swift @@ -23,6 +23,7 @@ extension Triple { } extension Triple { + @_spi(SwiftPMInternal) public func platformBuildPathComponent(buildSystem: BuildSystemProvider.Kind) -> String { // Use "apple" as the subdirectory because in theory Xcode build system // can be used to build for any Apple platform and it has its own diff --git a/Sources/SPMTestSupport/misc.swift b/Sources/SPMTestSupport/misc.swift index 536a0c670c6..f4d8e10dea1 100644 --- a/Sources/SPMTestSupport/misc.swift +++ b/Sources/SPMTestSupport/misc.swift @@ -319,7 +319,7 @@ private func swiftArgs( @available(*, deprecated, renamed: "loadModulesGraph", - message: "Rename for consistency: the type of this functions return value is named `ModulesGraph`." + message: "Renamed for consistency: the type of this functions return value is named `ModulesGraph`." ) public func loadPackageGraph( identityResolver: IdentityResolver = DefaultIdentityResolver(), diff --git a/Sources/XCBuildSupport/XCBuildDelegate.swift b/Sources/XCBuildSupport/XCBuildDelegate.swift index acf7f0c7317..cf96f3ef569 100644 --- a/Sources/XCBuildSupport/XCBuildDelegate.swift +++ b/Sources/XCBuildSupport/XCBuildDelegate.swift @@ -12,6 +12,8 @@ import Basics import Foundation + +@_spi(SwiftPMInternal) import SPMBuildCore import class TSCBasic.ThreadSafeOutputByteStream @@ -20,6 +22,7 @@ import protocol TSCBasic.OutputByteStream import enum TSCUtility.Diagnostics import protocol TSCUtility.ProgressAnimationProtocol +@_spi(SwiftPMInternal) public class XCBuildDelegate { private let buildSystem: SPMBuildCore.BuildSystem private var parser: XCBuildOutputParser! diff --git a/Sources/XCBuildSupport/XcodeBuildSystem.swift b/Sources/XCBuildSupport/XcodeBuildSystem.swift index 263027977f5..1ba41f012c3 100644 --- a/Sources/XCBuildSupport/XcodeBuildSystem.swift +++ b/Sources/XCBuildSupport/XcodeBuildSystem.swift @@ -28,6 +28,7 @@ import func TSCBasic.memoize import enum TSCUtility.Diagnostics +@_spi(SwiftPMInternal) public final class XcodeBuildSystem: SPMBuildCore.BuildSystem { private let buildParameters: BuildParameters private let packageGraphLoader: () throws -> ModulesGraph diff --git a/Sources/swift-bootstrap/main.swift b/Sources/swift-bootstrap/main.swift index 03d505d269d..1bdd1daf268 100644 --- a/Sources/swift-bootstrap/main.swift +++ b/Sources/swift-bootstrap/main.swift @@ -12,7 +12,10 @@ import ArgumentParser import Basics + +@_spi(SwiftPMInternal) import Build + import Dispatch @_spi(SwiftPMInternal) @@ -23,7 +26,11 @@ import OrderedCollections import PackageGraph import PackageLoading import PackageModel + +@_spi(SwiftPMInternal) import SPMBuildCore + +@_spi(SwiftPMInternal) import XCBuildSupport import struct TSCBasic.KeyedPair diff --git a/Sources/swift-build/Entrypoint.swift b/Sources/swift-build/Entrypoint.swift index 0dcb53302ec..12dfee5167b 100644 --- a/Sources/swift-build/Entrypoint.swift +++ b/Sources/swift-build/Entrypoint.swift @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +@_spi(SwiftPMInternal) import Commands @main diff --git a/Sources/swift-package-manager/SwiftPM.swift b/Sources/swift-package-manager/SwiftPM.swift index 213c5264ccb..3b0523ad728 100644 --- a/Sources/swift-package-manager/SwiftPM.swift +++ b/Sources/swift-package-manager/SwiftPM.swift @@ -11,7 +11,10 @@ //===----------------------------------------------------------------------===// import Basics + +@_spi(SwiftPMInternal) import Commands + import SwiftSDKCommand import PackageCollectionsCommand import PackageRegistryCommand diff --git a/Sources/swift-run/Entrypoint.swift b/Sources/swift-run/Entrypoint.swift index 4976cccdb15..8ddc1ea3da7 100644 --- a/Sources/swift-run/Entrypoint.swift +++ b/Sources/swift-run/Entrypoint.swift @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +@_spi(SwiftPMInternal) import Commands @main diff --git a/Sources/swift-test/main.swift b/Sources/swift-test/main.swift index 01cef92cb73..a74668facc3 100644 --- a/Sources/swift-test/main.swift +++ b/Sources/swift-test/main.swift @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +@_spi(SwiftPMInternal) import Commands SwiftTestCommand.main() diff --git a/Tests/BuildTests/BuildOperationTests.swift b/Tests/BuildTests/BuildOperationTests.swift index 3f196dd41fa..94061628aee 100644 --- a/Tests/BuildTests/BuildOperationTests.swift +++ b/Tests/BuildTests/BuildOperationTests.swift @@ -10,11 +10,19 @@ // //===----------------------------------------------------------------------===// -@testable import Build -@testable import PackageModel +@_spi(SwiftPMInternal) +@testable +import Build + +@testable +import PackageModel import Basics import SPMTestSupport + +@_spi(SwiftPMInternal) +import SPMBuildCore + import XCTest import class TSCBasic.BufferedOutputByteStream diff --git a/Tests/CommandsTests/BuildCommandTests.swift b/Tests/CommandsTests/BuildCommandTests.swift index 3221369719f..9af7b42a3a0 100644 --- a/Tests/CommandsTests/BuildCommandTests.swift +++ b/Tests/CommandsTests/BuildCommandTests.swift @@ -12,7 +12,11 @@ import Basics @testable import Commands -@testable import CoreCommands + +@_spi(SwiftPMInternal) +@testable +import CoreCommands + import PackageGraph import PackageLoading import PackageModel diff --git a/Tests/CommandsTests/MermaidPackageSerializerTests.swift b/Tests/CommandsTests/MermaidPackageSerializerTests.swift index 7427267895f..f8524d28c04 100644 --- a/Tests/CommandsTests/MermaidPackageSerializerTests.swift +++ b/Tests/CommandsTests/MermaidPackageSerializerTests.swift @@ -15,7 +15,7 @@ import class PackageModel.Manifest import struct PackageModel.ProductDescription import struct PackageModel.TargetDescription import class TSCBasic.InMemoryFileSystem -import func SPMTestSupport.loadPackageGraph +import func SPMTestSupport.loadModulesGraph import func SPMTestSupport.XCTAssertNoDiagnostics @testable @@ -31,7 +31,7 @@ final class MermaidPackageSerializerTests: XCTestCase { "/A/Sources/ATarget/main.swift", "/A/Tests/ATargetTests/TestCases.swift" ) - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fileSystem, manifests: [ Manifest.createRootManifest( @@ -76,7 +76,7 @@ final class MermaidPackageSerializerTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fileSystem, manifests: [ Manifest.createRootManifest( @@ -135,7 +135,7 @@ final class MermaidPackageSerializerTests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fileSystem, manifests: [ Manifest.createRootManifest( diff --git a/Tests/CommandsTests/PackageCommandTests.swift b/Tests/CommandsTests/PackageCommandTests.swift index c9b2f34b8a4..bc5ed0ccaf9 100644 --- a/Tests/CommandsTests/PackageCommandTests.swift +++ b/Tests/CommandsTests/PackageCommandTests.swift @@ -11,8 +11,14 @@ //===----------------------------------------------------------------------===// import Basics -@testable import CoreCommands -@testable import Commands + +@_spi(SwiftPMInternal) +@testable +import CoreCommands + +@testable +import Commands + import Foundation import PackageGraph import PackageLoading @@ -630,7 +636,7 @@ final class PackageCommandTests: CommandsTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fileSystem, manifests: [manifestA, manifestB, manifestC, manifestD], observabilityScope: observability.topScope diff --git a/Tests/CommandsTests/SwiftCommandStateTests.swift b/Tests/CommandsTests/SwiftCommandStateTests.swift index cbd580fa1fc..331cbfe1cc5 100644 --- a/Tests/CommandsTests/SwiftCommandStateTests.swift +++ b/Tests/CommandsTests/SwiftCommandStateTests.swift @@ -12,7 +12,11 @@ @testable import Basics @testable import Build -@testable import CoreCommands + +@_spi(SwiftPMInternal) +@testable +import CoreCommands + @testable import Commands @testable import PackageModel import SPMTestSupport @@ -242,7 +246,7 @@ final class SwiftCommandStateTests: CommandsTestCase { ]) let observer = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph(fileSystem: fs, manifests: [ + let graph = try loadModulesGraph(fileSystem: fs, manifests: [ Manifest.createRootManifest(displayName: "Pkg", path: "/Pkg", targets: [TargetDescription(name: "exe")]) From b9fc53b3e144e9bb32e2673ceac3755be817b477 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 27 Feb 2024 21:10:18 +0000 Subject: [PATCH 033/159] Adopt `package` in the test support module (#7372) All of the declarations in this module are never supposed to leak outside of the SwiftPM package. --- .../InMemoryGitRepository.swift | 124 +++++------ .../SPMTestSupport/ManifestExtensions.swift | 14 +- Sources/SPMTestSupport/MockArchiver.swift | 12 +- .../SPMTestSupport/MockBuildTestHelper.swift | 84 +++---- Sources/SPMTestSupport/MockDependency.swift | 32 +-- .../SPMTestSupport/MockDependencyGraph.swift | 14 +- Sources/SPMTestSupport/MockHTTPClient.swift | 2 +- .../SPMTestSupport/MockHashAlgorithm.swift | 10 +- .../SPMTestSupport/MockManifestLoader.swift | 28 +-- Sources/SPMTestSupport/MockPackage.swift | 30 +-- .../SPMTestSupport/MockPackageContainer.swift | 52 ++--- .../MockPackageFingerprintStorage.swift | 12 +- .../SPMTestSupport/MockPackageGraphs.swift | 12 +- .../MockPackageSigningEntityStorage.swift | 16 +- Sources/SPMTestSupport/MockProduct.swift | 8 +- Sources/SPMTestSupport/MockRegistry.swift | 22 +- Sources/SPMTestSupport/MockTarget.swift | 22 +- Sources/SPMTestSupport/MockWorkspace.swift | 206 +++++++++--------- Sources/SPMTestSupport/Observability.swift | 32 +-- Sources/SPMTestSupport/PIFTester.swift | 88 ++++---- ...ckageDependencyDescriptionExtensions.swift | 10 +- .../SPMTestSupport/PackageGraphTester.swift | 74 +++---- .../SPMTestSupport/ResolvedTarget+Mock.swift | 2 +- Sources/SPMTestSupport/Resolver.swift | 2 +- Sources/SPMTestSupport/SwiftPMProduct.swift | 14 +- Sources/SPMTestSupport/Toolchain.swift | 12 +- Sources/SPMTestSupport/XCTAssertHelpers.swift | 38 ++-- Sources/SPMTestSupport/misc.swift | 44 ++-- .../DependencyResolverPerfTests.swift | 2 +- 29 files changed, 507 insertions(+), 511 deletions(-) diff --git a/Sources/SPMTestSupport/InMemoryGitRepository.swift b/Sources/SPMTestSupport/InMemoryGitRepository.swift index 5d8ea5bc622..550dd3ed298 100644 --- a/Sources/SPMTestSupport/InMemoryGitRepository.swift +++ b/Sources/SPMTestSupport/InMemoryGitRepository.swift @@ -21,7 +21,7 @@ import struct TSCBasic.FileSystemError import class TSCBasic.InMemoryFileSystem /// The error encountered during in memory git repository operations. -public enum InMemoryGitRepositoryError: Swift.Error { +package enum InMemoryGitRepositoryError: Swift.Error { case unknownRevision case unknownTag case tagAlreadyPresent @@ -34,9 +34,9 @@ public enum InMemoryGitRepositoryError: Swift.Error { /// repository path, as well as on the file system interface of this class. /// Note: This class is intended to be used as testing infrastructure only. /// Note: This class is not thread safe yet. -public final class InMemoryGitRepository { +package final class InMemoryGitRepository { /// The revision identifier. - public typealias RevisionIdentifier = String + package typealias RevisionIdentifier = String /// A struct representing a revision state. Minimally it contains a hash identifier for the revision /// and the file system state. @@ -74,7 +74,7 @@ public final class InMemoryGitRepository { private let lock = NSLock() /// Create a new repository at the given path and filesystem. - public init(path: AbsolutePath, fs: InMemoryFileSystem) { + package init(path: AbsolutePath, fs: InMemoryFileSystem) { self.path = path self.fs = fs // Point head to a new revision state with empty hash to begin with. @@ -82,14 +82,14 @@ public final class InMemoryGitRepository { } /// The array of current tags in the repository. - public func getTags() throws -> [String] { + package func getTags() throws -> [String] { self.lock.withLock { Array(self.tagsMap.keys) } } /// The list of revisions in the repository. - public var revisions: [RevisionIdentifier] { + package var revisions: [RevisionIdentifier] { self.lock.withLock { Array(self.history.keys) } @@ -112,7 +112,7 @@ public final class InMemoryGitRepository { /// Commits the current state of the repository filesystem and returns the commit identifier. @discardableResult - public func commit(hash: String? = nil) throws -> String { + package func commit(hash: String? = nil) throws -> String { // Create a fake hash for this commit. let hash = hash ?? String((UUID().uuidString + UUID().uuidString).prefix(40)) self.lock.withLock { @@ -128,7 +128,7 @@ public final class InMemoryGitRepository { } /// Checks out the provided revision. - public func checkout(revision: RevisionIdentifier) throws { + package func checkout(revision: RevisionIdentifier) throws { guard let state = (self.lock.withLock { history[revision] }) else { throw InMemoryGitRepositoryError.unknownRevision } @@ -142,7 +142,7 @@ public final class InMemoryGitRepository { } /// Checks out a given tag. - public func checkout(tag: String) throws { + package func checkout(tag: String) throws { guard let hash = (self.lock.withLock { tagsMap[tag] }) else { throw InMemoryGitRepositoryError.unknownTag } @@ -191,7 +191,7 @@ public final class InMemoryGitRepository { } /// Tag the current HEAD with the given name. - public func tag(name: String) throws { + package func tag(name: String) throws { guard (self.lock.withLock { self.tagsMap[name] }) == nil else { throw InMemoryGitRepositoryError.tagAlreadyPresent } @@ -200,124 +200,124 @@ public final class InMemoryGitRepository { } } - public func hasUncommittedChanges() -> Bool { + package func hasUncommittedChanges() -> Bool { self.lock.withLock { isDirty } } - public func fetch() throws { + package func fetch() throws { // TODO. } } extension InMemoryGitRepository: FileSystem { - public func exists(_ path: TSCAbsolutePath, followSymlink: Bool) -> Bool { + package func exists(_ path: TSCAbsolutePath, followSymlink: Bool) -> Bool { self.lock.withLock { self.head.fileSystem.exists(path, followSymlink: followSymlink) } } - public func isDirectory(_ path: TSCAbsolutePath) -> Bool { + package func isDirectory(_ path: TSCAbsolutePath) -> Bool { self.lock.withLock { self.head.fileSystem.isDirectory(path) } } - public func isFile(_ path: TSCAbsolutePath) -> Bool { + package func isFile(_ path: TSCAbsolutePath) -> Bool { self.lock.withLock { self.head.fileSystem.isFile(path) } } - public func isSymlink(_ path: TSCAbsolutePath) -> Bool { + package func isSymlink(_ path: TSCAbsolutePath) -> Bool { self.lock.withLock { self.head.fileSystem.isSymlink(path) } } - public func isExecutableFile(_ path: TSCAbsolutePath) -> Bool { + package func isExecutableFile(_ path: TSCAbsolutePath) -> Bool { self.lock.withLock { self.head.fileSystem.isExecutableFile(path) } } - public func isReadable(_ path: TSCAbsolutePath) -> Bool { + package func isReadable(_ path: TSCAbsolutePath) -> Bool { return self.exists(path) } - public func isWritable(_ path: TSCAbsolutePath) -> Bool { + package func isWritable(_ path: TSCAbsolutePath) -> Bool { return false } - public var currentWorkingDirectory: TSCAbsolutePath? { + package var currentWorkingDirectory: TSCAbsolutePath? { return .root } - public func changeCurrentWorkingDirectory(to path: TSCAbsolutePath) throws { + package func changeCurrentWorkingDirectory(to path: TSCAbsolutePath) throws { throw FileSystemError(.unsupported, path) } - public var homeDirectory: TSCAbsolutePath { + package var homeDirectory: TSCAbsolutePath { fatalError("Unsupported") } - public var cachesDirectory: TSCAbsolutePath? { + package var cachesDirectory: TSCAbsolutePath? { fatalError("Unsupported") } - public var tempDirectory: TSCAbsolutePath { + package var tempDirectory: TSCAbsolutePath { fatalError("Unsupported") } - public func getDirectoryContents(_ path: TSCAbsolutePath) throws -> [String] { + package func getDirectoryContents(_ path: TSCAbsolutePath) throws -> [String] { try self.lock.withLock { try self.head.fileSystem.getDirectoryContents(path) } } - public func createDirectory(_ path: TSCAbsolutePath, recursive: Bool) throws { + package func createDirectory(_ path: TSCAbsolutePath, recursive: Bool) throws { try self.lock.withLock { try self.head.fileSystem.createDirectory(path, recursive: recursive) } } - public func createSymbolicLink(_ path: TSCAbsolutePath, pointingAt destination: TSCAbsolutePath, relative: Bool) throws { + package func createSymbolicLink(_ path: TSCAbsolutePath, pointingAt destination: TSCAbsolutePath, relative: Bool) throws { throw FileSystemError(.unsupported, path) } - public func readFileContents(_ path: TSCAbsolutePath) throws -> ByteString { + package func readFileContents(_ path: TSCAbsolutePath) throws -> ByteString { try self.lock.withLock { return try head.fileSystem.readFileContents(path) } } - public func writeFileContents(_ path: TSCAbsolutePath, bytes: ByteString) throws { + package func writeFileContents(_ path: TSCAbsolutePath, bytes: ByteString) throws { try self.lock.withLock { try self.head.fileSystem.writeFileContents(path, bytes: bytes) self.isDirty = true } } - public func removeFileTree(_ path: TSCAbsolutePath) throws { + package func removeFileTree(_ path: TSCAbsolutePath) throws { try self.lock.withLock { try self.head.fileSystem.removeFileTree(path) } } - public func chmod(_ mode: FileMode, path: TSCAbsolutePath, options: Set) throws { + package func chmod(_ mode: FileMode, path: TSCAbsolutePath, options: Set) throws { try self.lock.withLock { try self.head.fileSystem.chmod(mode, path: path, options: options) } } - public func copy(from sourcePath: TSCAbsolutePath, to destinationPath: TSCAbsolutePath) throws { + package func copy(from sourcePath: TSCAbsolutePath, to destinationPath: TSCAbsolutePath) throws { try self.lock.withLock { try self.head.fileSystem.copy(from: sourcePath, to: destinationPath) } } - public func move(from sourcePath: TSCAbsolutePath, to destinationPath: TSCAbsolutePath) throws { + package func move(from sourcePath: TSCAbsolutePath, to destinationPath: TSCAbsolutePath) throws { try self.lock.withLock { try self.head.fileSystem.move(from: sourcePath, to: destinationPath) } @@ -325,7 +325,7 @@ extension InMemoryGitRepository: FileSystem { } extension InMemoryGitRepository: Repository { - public func resolveRevision(tag: String) throws -> Revision { + package func resolveRevision(tag: String) throws -> Revision { try self.lock.withLock { guard let revision = self.tagsMap[tag] else { throw InternalError("unknown tag \(tag)") @@ -334,19 +334,19 @@ extension InMemoryGitRepository: Repository { } } - public func resolveRevision(identifier: String) throws -> Revision { + package func resolveRevision(identifier: String) throws -> Revision { self.lock.withLock { return Revision(identifier: self.tagsMap[identifier] ?? identifier) } } - public func exists(revision: Revision) -> Bool { + package func exists(revision: Revision) -> Bool { self.lock.withLock { return self.history[revision.identifier] != nil } } - public func openFileView(revision: Revision) throws -> FileSystem { + package func openFileView(revision: Revision) throws -> FileSystem { try self.lock.withLock { guard let entry = self.history[revision.identifier] else { throw InternalError("unknown revision \(revision)") @@ -355,70 +355,70 @@ extension InMemoryGitRepository: Repository { } } - public func openFileView(tag: String) throws -> FileSystem { + package func openFileView(tag: String) throws -> FileSystem { let revision = try self.resolveRevision(tag: tag) return try self.openFileView(revision: revision) } } extension InMemoryGitRepository: WorkingCheckout { - public func getCurrentRevision() throws -> Revision { + package func getCurrentRevision() throws -> Revision { self.lock.withLock { return Revision(identifier: self.head.hash) } } - public func checkout(revision: Revision) throws { + package func checkout(revision: Revision) throws { // will lock try checkout(revision: revision.identifier) } - public func hasUnpushedCommits() throws -> Bool { + package func hasUnpushedCommits() throws -> Bool { return false } - public func checkout(newBranch: String) throws { + package func checkout(newBranch: String) throws { self.lock.withLock { self.history[newBranch] = head } } - public func isAlternateObjectStoreValid(expected: AbsolutePath) -> Bool { + package func isAlternateObjectStoreValid(expected: AbsolutePath) -> Bool { return true } - public func areIgnored(_ paths: [AbsolutePath]) throws -> [Bool] { + package func areIgnored(_ paths: [AbsolutePath]) throws -> [Bool] { return [false] } } -// Public mutation of `InMemoryGitRepository` is protected with a lock. +// package mutation of `InMemoryGitRepository` is protected with a lock. extension InMemoryGitRepository: @unchecked Sendable {} /// This class implement provider for in memory git repository. -public final class InMemoryGitRepositoryProvider: RepositoryProvider { +package final class InMemoryGitRepositoryProvider: RepositoryProvider { /// Contains the repository added to this provider. - public var specifierMap = ThreadSafeKeyValueStore() + package var specifierMap = ThreadSafeKeyValueStore() /// Contains the repositories which are fetched using this provider. - public var fetchedMap = ThreadSafeKeyValueStore() + package var fetchedMap = ThreadSafeKeyValueStore() /// Contains the repositories which are checked out using this provider. - public var checkoutsMap = ThreadSafeKeyValueStore() + package var checkoutsMap = ThreadSafeKeyValueStore() /// Create a new provider. - public init() { + package init() { } /// Add a repository to this provider. Only the repositories added with this interface can be operated on /// with this provider. - public func add(specifier: RepositorySpecifier, repository: InMemoryGitRepository) { + package func add(specifier: RepositorySpecifier, repository: InMemoryGitRepository) { // Save the repository in specifier map. specifierMap[specifier] = repository } /// This method returns the stored reference to the git repository which was fetched or checked out. - public func openRepo(at path: AbsolutePath) throws -> InMemoryGitRepository { + package func openRepo(at path: AbsolutePath) throws -> InMemoryGitRepository { if let fetch = fetchedMap[path] { return fetch } @@ -431,7 +431,7 @@ public final class InMemoryGitRepositoryProvider: RepositoryProvider { // MARK: - RepositoryProvider conformance // Note: These methods use force unwrap (instead of throwing) to honor their preconditions. - public func fetch(repository: RepositorySpecifier, to path: AbsolutePath, progressHandler: FetchProgress.Handler? = nil) throws { + package func fetch(repository: RepositorySpecifier, to path: AbsolutePath, progressHandler: FetchProgress.Handler? = nil) throws { guard let repo = specifierMap[RepositorySpecifier(location: repository.location)] else { throw InternalError("unknown repo at \(repository.location)") } @@ -439,25 +439,25 @@ public final class InMemoryGitRepositoryProvider: RepositoryProvider { add(specifier: RepositorySpecifier(path: path), repository: repo) } - public func repositoryExists(at path: AbsolutePath) throws -> Bool { + package func repositoryExists(at path: AbsolutePath) throws -> Bool { return fetchedMap[path] != nil } - public func copy(from sourcePath: AbsolutePath, to destinationPath: AbsolutePath) throws { + package func copy(from sourcePath: AbsolutePath, to destinationPath: AbsolutePath) throws { guard let repo = fetchedMap[sourcePath] else { throw InternalError("unknown repo at \(sourcePath)") } fetchedMap[destinationPath] = try repo.copy() } - public func open(repository: RepositorySpecifier, at path: AbsolutePath) throws -> Repository { + package func open(repository: RepositorySpecifier, at path: AbsolutePath) throws -> Repository { guard let repository = self.fetchedMap[path] else { throw InternalError("unknown repository at \(path)") } return repository } - public func createWorkingCopy( + package func createWorkingCopy( repository: RepositorySpecifier, sourcePath: AbsolutePath, at destinationPath: AbsolutePath, @@ -471,26 +471,26 @@ public final class InMemoryGitRepositoryProvider: RepositoryProvider { return copy } - public func workingCopyExists(at path: AbsolutePath) throws -> Bool { + package func workingCopyExists(at path: AbsolutePath) throws -> Bool { return checkoutsMap.contains(path) } - public func openWorkingCopy(at path: AbsolutePath) throws -> WorkingCheckout { + package func openWorkingCopy(at path: AbsolutePath) throws -> WorkingCheckout { guard let checkout = checkoutsMap[path] else { throw InternalError("unknown checkout at \(path)") } return checkout } - public func isValidDirectory(_ directory: AbsolutePath) throws -> Bool { + package func isValidDirectory(_ directory: AbsolutePath) throws -> Bool { return true } - public func isValidDirectory(_ directory: AbsolutePath, for repository: RepositorySpecifier) throws -> Bool { + package func isValidDirectory(_ directory: AbsolutePath, for repository: RepositorySpecifier) throws -> Bool { return true } - public func cancel(deadline: DispatchTime) throws { + package func cancel(deadline: DispatchTime) throws { // noop } } diff --git a/Sources/SPMTestSupport/ManifestExtensions.swift b/Sources/SPMTestSupport/ManifestExtensions.swift index 30cbd5bc3a2..0d51ed6c9e7 100644 --- a/Sources/SPMTestSupport/ManifestExtensions.swift +++ b/Sources/SPMTestSupport/ManifestExtensions.swift @@ -17,7 +17,7 @@ import PackageModel import struct TSCUtility.Version extension Manifest { - public static func createRootManifest( + package static func createRootManifest( displayName: String, path: AbsolutePath = .root, defaultLocalization: String? = nil, @@ -53,7 +53,7 @@ extension Manifest { ) } - public static func createFileSystemManifest( + package static func createFileSystemManifest( displayName: String, path: AbsolutePath, defaultLocalization: String? = nil, @@ -89,7 +89,7 @@ extension Manifest { ) } - public static func createLocalSourceControlManifest( + package static func createLocalSourceControlManifest( displayName: String, path: AbsolutePath, defaultLocalization: String? = nil, @@ -125,7 +125,7 @@ extension Manifest { ) } - public static func createRemoteSourceControlManifest( + package static func createRemoteSourceControlManifest( displayName: String, url: SourceControlURL, path: AbsolutePath, @@ -162,7 +162,7 @@ extension Manifest { ) } - public static func createRegistryManifest( + package static func createRegistryManifest( displayName: String, identity: PackageIdentity, path: AbsolutePath = .root, @@ -199,7 +199,7 @@ extension Manifest { ) } - public static func createManifest( + package static func createManifest( displayName: String, path: AbsolutePath = .root, packageKind: PackageReference.Kind, @@ -238,7 +238,7 @@ extension Manifest { ) } - public func with(location: String) -> Manifest { + package func with(location: String) -> Manifest { Manifest( displayName: self.displayName, path: self.path, diff --git a/Sources/SPMTestSupport/MockArchiver.swift b/Sources/SPMTestSupport/MockArchiver.swift index cf1a84434df..209bf9bab00 100644 --- a/Sources/SPMTestSupport/MockArchiver.swift +++ b/Sources/SPMTestSupport/MockArchiver.swift @@ -28,20 +28,20 @@ package class MockArchiver: Archiver { package typealias ValidationHandler = (MockArchiver, AbsolutePath, (Result) -> Void) throws -> Void package struct Extraction: Equatable { - public let archivePath: AbsolutePath - public let destinationPath: AbsolutePath + package let archivePath: AbsolutePath + package let destinationPath: AbsolutePath - public init(archivePath: AbsolutePath, destinationPath: AbsolutePath) { + package init(archivePath: AbsolutePath, destinationPath: AbsolutePath) { self.archivePath = archivePath self.destinationPath = destinationPath } } package struct Compression: Equatable { - public let directory: AbsolutePath - public let destinationPath: AbsolutePath + package let directory: AbsolutePath + package let destinationPath: AbsolutePath - public init(directory: AbsolutePath, destinationPath: AbsolutePath) { + package init(directory: AbsolutePath, destinationPath: AbsolutePath) { self.directory = directory self.destinationPath = destinationPath } diff --git a/Sources/SPMTestSupport/MockBuildTestHelper.swift b/Sources/SPMTestSupport/MockBuildTestHelper.swift index 2aeb77764ff..38629590d6c 100644 --- a/Sources/SPMTestSupport/MockBuildTestHelper.swift +++ b/Sources/SPMTestSupport/MockBuildTestHelper.swift @@ -20,31 +20,31 @@ import SPMBuildCore import TSCUtility import XCTest -public struct MockToolchain: PackageModel.Toolchain { +package struct MockToolchain: PackageModel.Toolchain { #if os(Windows) - public let librarianPath = AbsolutePath("/fake/path/to/link.exe") + package let librarianPath = AbsolutePath("/fake/path/to/link.exe") #elseif os(iOS) || os(macOS) || os(tvOS) || os(watchOS) - public let librarianPath = AbsolutePath("/fake/path/to/libtool") + package let librarianPath = AbsolutePath("/fake/path/to/libtool") #else - public let librarianPath = AbsolutePath("/fake/path/to/llvm-ar") + package let librarianPath = AbsolutePath("/fake/path/to/llvm-ar") #endif - public let swiftCompilerPath = AbsolutePath("/fake/path/to/swiftc") - public let includeSearchPaths = [AbsolutePath]() - public let librarySearchPaths = [AbsolutePath]() - public let swiftResourcesPath: AbsolutePath? = nil - public let swiftStaticResourcesPath: AbsolutePath? = nil - public let isSwiftDevelopmentToolchain = false - public let sdkRootPath: AbsolutePath? = nil - public let swiftPluginServerPath: AbsolutePath? = nil - public let extraFlags = PackageModel.BuildFlags() - public let installedSwiftPMConfiguration = InstalledSwiftPMConfiguration.default - public let providedLibraries = [LibraryMetadata]() - - public func getClangCompiler() throws -> AbsolutePath { + package let swiftCompilerPath = AbsolutePath("/fake/path/to/swiftc") + package let includeSearchPaths = [AbsolutePath]() + package let librarySearchPaths = [AbsolutePath]() + package let swiftResourcesPath: AbsolutePath? = nil + package let swiftStaticResourcesPath: AbsolutePath? = nil + package let isSwiftDevelopmentToolchain = false + package let sdkRootPath: AbsolutePath? = nil + package let swiftPluginServerPath: AbsolutePath? = nil + package let extraFlags = PackageModel.BuildFlags() + package let installedSwiftPMConfiguration = InstalledSwiftPMConfiguration.default + package let providedLibraries = [LibraryMetadata]() + + package func getClangCompiler() throws -> AbsolutePath { "/fake/path/to/clang" } - public func _isClangCompilerVendorApple() throws -> Bool? { + package func _isClangCompilerVendorApple() throws -> Bool? { #if os(macOS) return true #else @@ -52,27 +52,27 @@ public struct MockToolchain: PackageModel.Toolchain { #endif } - public init() {} + package init() {} } extension Basics.Triple { - public static let x86_64MacOS = try! Self("x86_64-apple-macosx") - public static let x86_64Linux = try! Self("x86_64-unknown-linux-gnu") - public static let arm64Linux = try! Self("aarch64-unknown-linux-gnu") - public static let arm64Android = try! Self("aarch64-unknown-linux-android") - public static let windows = try! Self("x86_64-unknown-windows-msvc") - public static let wasi = try! Self("wasm32-unknown-wasi") - public static let arm64iOS = try! Self("arm64-apple-ios") + package static let x86_64MacOS = try! Self("x86_64-apple-macosx") + package static let x86_64Linux = try! Self("x86_64-unknown-linux-gnu") + package static let arm64Linux = try! Self("aarch64-unknown-linux-gnu") + package static let arm64Android = try! Self("aarch64-unknown-linux-android") + package static let windows = try! Self("x86_64-unknown-windows-msvc") + package static let wasi = try! Self("wasm32-unknown-wasi") + package static let arm64iOS = try! Self("arm64-apple-ios") } -public let hostTriple = try! UserToolchain.default.targetTriple +package let hostTriple = try! UserToolchain.default.targetTriple #if os(macOS) -public let defaultTargetTriple: String = hostTriple.tripleString(forPlatformVersion: "10.13") +package let defaultTargetTriple: String = hostTriple.tripleString(forPlatformVersion: "10.13") #else -public let defaultTargetTriple: String = hostTriple.tripleString +package let defaultTargetTriple: String = hostTriple.tripleString #endif -public func mockBuildParameters( +package func mockBuildParameters( buildPath: AbsolutePath = "/path/to/build", config: BuildConfiguration = .debug, toolchain: PackageModel.Toolchain = MockToolchain(), @@ -114,7 +114,7 @@ public func mockBuildParameters( ) } -public func mockBuildParameters(environment: BuildEnvironment) -> BuildParameters { +package func mockBuildParameters(environment: BuildEnvironment) -> BuildParameters { let triple: Basics.Triple switch environment.platform { case .macOS: @@ -136,12 +136,12 @@ enum BuildError: Swift.Error { case error(String) } -public struct BuildPlanResult { - public let plan: Build.BuildPlan - public let targetMap: [String: TargetBuildDescription] - public let productMap: [String: Build.ProductBuildDescription] +package struct BuildPlanResult { + package let plan: Build.BuildPlan + package let targetMap: [String: TargetBuildDescription] + package let productMap: [String: Build.ProductBuildDescription] - public init(plan: Build.BuildPlan) throws { + package init(plan: Build.BuildPlan) throws { self.plan = plan self.productMap = try Dictionary( throwingUniqueKeysWithValues: plan.buildProducts @@ -161,22 +161,22 @@ public struct BuildPlanResult { ) } - public func checkTargetsCount(_ count: Int, file: StaticString = #file, line: UInt = #line) { + package func checkTargetsCount(_ count: Int, file: StaticString = #file, line: UInt = #line) { XCTAssertEqual(self.plan.targetMap.count, count, file: file, line: line) } - public func checkProductsCount(_ count: Int, file: StaticString = #file, line: UInt = #line) { + package func checkProductsCount(_ count: Int, file: StaticString = #file, line: UInt = #line) { XCTAssertEqual(self.plan.productMap.count, count, file: file, line: line) } - public func target(for name: String) throws -> TargetBuildDescription { + package func target(for name: String) throws -> TargetBuildDescription { guard let target = targetMap[name] else { throw BuildError.error("Target \(name) not found.") } return target } - public func buildProduct(for name: String) throws -> Build.ProductBuildDescription { + package func buildProduct(for name: String) throws -> Build.ProductBuildDescription { guard let product = productMap[name] else { // Display the thrown error on macOS throw BuildError.error("Product \(name) not found.") @@ -186,7 +186,7 @@ public struct BuildPlanResult { } extension TargetBuildDescription { - public func swiftTarget() throws -> SwiftTargetBuildDescription { + package func swiftTarget() throws -> SwiftTargetBuildDescription { switch self { case .swift(let target): return target @@ -195,7 +195,7 @@ extension TargetBuildDescription { } } - public func clangTarget() throws -> ClangTargetBuildDescription { + package func clangTarget() throws -> ClangTargetBuildDescription { switch self { case .clang(let target): return target diff --git a/Sources/SPMTestSupport/MockDependency.swift b/Sources/SPMTestSupport/MockDependency.swift index 5d519724217..321f54214a9 100644 --- a/Sources/SPMTestSupport/MockDependency.swift +++ b/Sources/SPMTestSupport/MockDependency.swift @@ -15,13 +15,13 @@ import Foundation import PackageLoading import PackageModel -public typealias SourceControlRequirement = PackageDependency.SourceControl.Requirement -public typealias RegistryRequirement = PackageDependency.Registry.Requirement +package typealias SourceControlRequirement = PackageDependency.SourceControl.Requirement +package typealias RegistryRequirement = PackageDependency.Registry.Requirement -public struct MockDependency { - public let deprecatedName: String? - public let location: Location - public let products: ProductFilter +package struct MockDependency { + package let deprecatedName: String? + package let location: Location + package let products: ProductFilter init(deprecatedName: String? = nil, location: Location, products: ProductFilter = .everything) { self.deprecatedName = deprecatedName @@ -29,7 +29,7 @@ public struct MockDependency { self.products = products } - public func convert(baseURL: AbsolutePath, identityResolver: IdentityResolver) throws -> PackageDependency { + package func convert(baseURL: AbsolutePath, identityResolver: IdentityResolver) throws -> PackageDependency { switch self.location { case .fileSystem(let path): let absolutePath = baseURL.appending(path) @@ -119,39 +119,39 @@ public struct MockDependency { } - public static func fileSystem(path: String, products: ProductFilter = .everything) -> MockDependency { + package static func fileSystem(path: String, products: ProductFilter = .everything) -> MockDependency { try! MockDependency(location: .fileSystem(path: RelativePath(validating: path)), products: products) } - public static func sourceControl(path: String, requirement: SourceControlRequirement, products: ProductFilter = .everything) -> MockDependency { + package static func sourceControl(path: String, requirement: SourceControlRequirement, products: ProductFilter = .everything) -> MockDependency { try! .sourceControl(path: RelativePath(validating: path), requirement: requirement, products: products) } - public static func sourceControl(path: RelativePath, requirement: SourceControlRequirement, products: ProductFilter = .everything) -> MockDependency { + package static func sourceControl(path: RelativePath, requirement: SourceControlRequirement, products: ProductFilter = .everything) -> MockDependency { MockDependency(location: .localSourceControl(path: path, requirement: requirement), products: products) } - public static func sourceControlWithDeprecatedName(name: String, path: String, requirement: SourceControlRequirement, products: ProductFilter = .everything) -> MockDependency { + package static func sourceControlWithDeprecatedName(name: String, path: String, requirement: SourceControlRequirement, products: ProductFilter = .everything) -> MockDependency { try! MockDependency(deprecatedName: name, location: .localSourceControl(path: RelativePath(validating: path), requirement: requirement), products: products) } - public static func sourceControl(url: String, requirement: SourceControlRequirement, products: ProductFilter = .everything) -> MockDependency { + package static func sourceControl(url: String, requirement: SourceControlRequirement, products: ProductFilter = .everything) -> MockDependency { .sourceControl(url: SourceControlURL(url), requirement: requirement, products: products) } - public static func sourceControl(url: SourceControlURL, requirement: SourceControlRequirement, products: ProductFilter = .everything) -> MockDependency { + package static func sourceControl(url: SourceControlURL, requirement: SourceControlRequirement, products: ProductFilter = .everything) -> MockDependency { MockDependency(location: .remoteSourceControl(url: url, requirement: requirement), products: products) } - public static func registry(identity: String, requirement: RegistryRequirement, products: ProductFilter = .everything) -> MockDependency { + package static func registry(identity: String, requirement: RegistryRequirement, products: ProductFilter = .everything) -> MockDependency { .registry(identity: .plain(identity), requirement: requirement) } - public static func registry(identity: PackageIdentity, requirement: RegistryRequirement, products: ProductFilter = .everything) -> MockDependency { + package static func registry(identity: PackageIdentity, requirement: RegistryRequirement, products: ProductFilter = .everything) -> MockDependency { MockDependency(location: .registry(identity: identity, requirement: requirement), products: products) } - public enum Location { + package enum Location { case fileSystem(path: RelativePath) case localSourceControl(path: RelativePath, requirement: SourceControlRequirement) case remoteSourceControl(url: SourceControlURL, requirement: SourceControlRequirement) diff --git a/Sources/SPMTestSupport/MockDependencyGraph.swift b/Sources/SPMTestSupport/MockDependencyGraph.swift index 6a17968ab0b..5b2c8488654 100644 --- a/Sources/SPMTestSupport/MockDependencyGraph.swift +++ b/Sources/SPMTestSupport/MockDependencyGraph.swift @@ -16,20 +16,20 @@ import PackageModel import struct TSCUtility.Version -public struct MockDependencyGraph { - public let name: String - public let constraints: [MockPackageContainer.Constraint] - public let containers: [MockPackageContainer] - public let result: [PackageReference: Version] +package struct MockDependencyGraph { + package let name: String + package let constraints: [MockPackageContainer.Constraint] + package let containers: [MockPackageContainer] + package let result: [PackageReference: Version] - public init(name: String, constraints: [MockPackageContainer.Constraint], containers: [MockPackageContainer], result: [PackageReference : Version]) { + package init(name: String, constraints: [MockPackageContainer.Constraint], containers: [MockPackageContainer], result: [PackageReference : Version]) { self.name = name self.constraints = constraints self.containers = containers self.result = result } - public func checkResult( + package func checkResult( _ output: [(container: PackageReference, version: Version)], file: StaticString = #file, line: UInt = #line diff --git a/Sources/SPMTestSupport/MockHTTPClient.swift b/Sources/SPMTestSupport/MockHTTPClient.swift index c680bcc2612..87474faf0dc 100644 --- a/Sources/SPMTestSupport/MockHTTPClient.swift +++ b/Sources/SPMTestSupport/MockHTTPClient.swift @@ -13,7 +13,7 @@ import Basics extension LegacyHTTPClient { - public static func mock(fileSystem: FileSystem) -> LegacyHTTPClient { + package static func mock(fileSystem: FileSystem) -> LegacyHTTPClient { let handler: LegacyHTTPClient.Handler = { request, _, completion in switch request.kind { case.generic: diff --git a/Sources/SPMTestSupport/MockHashAlgorithm.swift b/Sources/SPMTestSupport/MockHashAlgorithm.swift index 4d8c88e4e81..374e4c77874 100644 --- a/Sources/SPMTestSupport/MockHashAlgorithm.swift +++ b/Sources/SPMTestSupport/MockHashAlgorithm.swift @@ -15,17 +15,17 @@ import Basics import struct TSCBasic.ByteString import protocol TSCBasic.HashAlgorithm -public final class MockHashAlgorithm { - public typealias Handler = @Sendable (ByteString) -> ByteString +package final class MockHashAlgorithm { + package typealias Handler = @Sendable (ByteString) -> ByteString - public let hashes = ThreadSafeArrayStore() + package let hashes = ThreadSafeArrayStore() private let handler: Handler? - public init(handler: Handler? = nil) { + package init(handler: Handler? = nil) { self.handler = handler } - public func hash(_ hash: ByteString) -> ByteString { + package func hash(_ hash: ByteString) -> ByteString { if let handler = self.handler { return handler(hash) } else { diff --git a/Sources/SPMTestSupport/MockManifestLoader.swift b/Sources/SPMTestSupport/MockManifestLoader.swift index 8775dedbfa5..fc1f345ed64 100644 --- a/Sources/SPMTestSupport/MockManifestLoader.swift +++ b/Sources/SPMTestSupport/MockManifestLoader.swift @@ -21,7 +21,7 @@ import func XCTest.XCTFail import enum TSCBasic.ProcessEnv import struct TSCUtility.Version -public enum MockManifestLoaderError: Swift.Error { +package enum MockManifestLoaderError: Swift.Error { case unknownRequest(String) } @@ -34,24 +34,24 @@ public enum MockManifestLoaderError: Swift.Error { /// /// This implementation will throw an error if a request to load an unknown /// manifest is made. -public final class MockManifestLoader: ManifestLoaderProtocol { - public struct Key: Hashable { - public let url: String - public let version: Version? +package final class MockManifestLoader: ManifestLoaderProtocol { + package struct Key: Hashable { + package let url: String + package let version: Version? - public init(url: String, version: Version? = nil) { + package init(url: String, version: Version? = nil) { self.url = url self.version = version } } - public let manifests: ThreadSafeKeyValueStore + package let manifests: ThreadSafeKeyValueStore - public init(manifests: [Key: Manifest]) { + package init(manifests: [Key: Manifest]) { self.manifests = ThreadSafeKeyValueStore(manifests) } - public func load( + package func load( manifestPath: AbsolutePath, manifestToolsVersion: ToolsVersion, packageIdentity: PackageIdentity, @@ -76,12 +76,12 @@ public final class MockManifestLoader: ManifestLoaderProtocol { } } - public func resetCache(observabilityScope: ObservabilityScope) {} - public func purgeCache(observabilityScope: ObservabilityScope) {} + package func resetCache(observabilityScope: ObservabilityScope) {} + package func purgeCache(observabilityScope: ObservabilityScope) {} } extension ManifestLoader { - public func load( + package func load( manifestPath: AbsolutePath, packageKind: PackageReference.Kind, toolsVersion manifestToolsVersion: ToolsVersion, @@ -128,7 +128,7 @@ extension ManifestLoader { } extension ManifestLoader { - public func load( + package func load( packagePath: AbsolutePath, packageKind: PackageReference.Kind, currentToolsVersion: ToolsVersion, @@ -180,7 +180,7 @@ extension ManifestLoader { /// between threads. This means that when this method is called simultaneously /// from different threads, the environment will neither be setup nor restored /// correctly. -public func withCustomEnv(_ env: [String: String], body: () async throws -> Void) async throws { +package func withCustomEnv(_ env: [String: String], body: () async throws -> Void) async throws { let state = env.map { ($0, $1) } let restore = { for (key, value) in state { diff --git a/Sources/SPMTestSupport/MockPackage.swift b/Sources/SPMTestSupport/MockPackage.swift index d692b883930..ed56edcb2a7 100644 --- a/Sources/SPMTestSupport/MockPackage.swift +++ b/Sources/SPMTestSupport/MockPackage.swift @@ -14,20 +14,20 @@ import Basics import Foundation import PackageModel -public struct MockPackage { - public let name: String - public let platforms: [PlatformDescription] - public let location: Location - public let targets: [MockTarget] - public let products: [MockProduct] - public let dependencies: [MockDependency] - public let versions: [String?] +package struct MockPackage { + package let name: String + package let platforms: [PlatformDescription] + package let location: Location + package let targets: [MockTarget] + package let products: [MockProduct] + package let dependencies: [MockDependency] + package let versions: [String?] /// Provides revision identifier for the given version. A random identifier might be assigned if this is nil. - public let revisionProvider: ((String) -> String)? + package let revisionProvider: ((String) -> String)? // FIXME: This should be per-version. - public let toolsVersion: ToolsVersion? + package let toolsVersion: ToolsVersion? - public init( + package init( name: String, platforms: [PlatformDescription] = [], path: String? = nil, @@ -50,7 +50,7 @@ public struct MockPackage { self.toolsVersion = toolsVersion } - public init( + package init( name: String, platforms: [PlatformDescription] = [], url: String, @@ -72,7 +72,7 @@ public struct MockPackage { self.toolsVersion = toolsVersion } - public init( + package init( name: String, platforms: [PlatformDescription] = [], identity: String, @@ -100,7 +100,7 @@ public struct MockPackage { self.toolsVersion = toolsVersion } - public static func genericPackage(named name: String) throws -> MockPackage { + package static func genericPackage(named name: String) throws -> MockPackage { return MockPackage( name: name, targets: [ @@ -113,7 +113,7 @@ public struct MockPackage { ) } - public enum Location { + package enum Location { case fileSystem(path: RelativePath) case sourceControl(url: SourceControlURL) case registry(identity: PackageIdentity, alternativeURLs: [URL]?, metadata: RegistryReleaseMetadata?) diff --git a/Sources/SPMTestSupport/MockPackageContainer.swift b/Sources/SPMTestSupport/MockPackageContainer.swift index 730ddffe12b..0dc608943fc 100644 --- a/Sources/SPMTestSupport/MockPackageContainer.swift +++ b/Sources/SPMTestSupport/MockPackageContainer.swift @@ -19,12 +19,12 @@ import XCTest import struct TSCUtility.Version -public class MockPackageContainer: CustomPackageContainer { - public typealias Constraint = PackageContainerConstraint +package class MockPackageContainer: CustomPackageContainer { + package typealias Constraint = PackageContainerConstraint - public typealias Dependency = (container: PackageReference, requirement: PackageRequirement) + package typealias Dependency = (container: PackageReference, requirement: PackageRequirement) - public let package: PackageReference + package let package: PackageReference let dependencies: [String: [Dependency]] let filteredMode: Bool @@ -32,26 +32,26 @@ public class MockPackageContainer: CustomPackageContainer { let fileSystem: FileSystem? let customRetrievalPath: AbsolutePath? - public var unversionedDeps: [MockPackageContainer.Constraint] = [] + package var unversionedDeps: [MockPackageContainer.Constraint] = [] /// Contains the versions for which the dependencies were requested by resolver using getDependencies(). - public var requestedVersions: Set = [] + package var requestedVersions: Set = [] - public let _versions: [Version] - public func toolsVersionsAppropriateVersionsDescending() throws -> [Version] { + package let _versions: [Version] + package func toolsVersionsAppropriateVersionsDescending() throws -> [Version] { return try self.versionsDescending() } - public func versionsAscending() throws -> [Version] { + package func versionsAscending() throws -> [Version] { return _versions } - public func getDependencies(at version: Version, productFilter: ProductFilter) -> [MockPackageContainer.Constraint] { + package func getDependencies(at version: Version, productFilter: ProductFilter) -> [MockPackageContainer.Constraint] { requestedVersions.insert(version) return getDependencies(at: version.description, productFilter: productFilter) } - public func getDependencies(at revision: String, productFilter: ProductFilter) -> [MockPackageContainer.Constraint] { + package func getDependencies(at revision: String, productFilter: ProductFilter) -> [MockPackageContainer.Constraint] { let dependencies: [Dependency] if filteredMode { dependencies = filteredDependencies[productFilter]! @@ -64,27 +64,27 @@ public class MockPackageContainer: CustomPackageContainer { } } - public func getUnversionedDependencies(productFilter: ProductFilter) -> [MockPackageContainer.Constraint] { + package func getUnversionedDependencies(productFilter: ProductFilter) -> [MockPackageContainer.Constraint] { return unversionedDeps } - public func loadPackageReference(at boundVersion: BoundVersion) throws -> PackageReference { + package func loadPackageReference(at boundVersion: BoundVersion) throws -> PackageReference { return self.package } - public func isToolsVersionCompatible(at version: Version) -> Bool { + package func isToolsVersionCompatible(at version: Version) -> Bool { return true } - public func toolsVersion(for version: Version) throws -> ToolsVersion { + package func toolsVersion(for version: Version) throws -> ToolsVersion { return ToolsVersion.current } - public var isRemoteContainer: Bool? { + package var isRemoteContainer: Bool? { return true } - public func retrieve(at version: Version, progressHandler: ((Int64, Int64?) -> Void)?, observabilityScope: ObservabilityScope) throws -> AbsolutePath { + package func retrieve(at version: Version, progressHandler: ((Int64, Int64?) -> Void)?, observabilityScope: ObservabilityScope) throws -> AbsolutePath { if let customRetrievalPath { return customRetrievalPath } else { @@ -92,11 +92,11 @@ public class MockPackageContainer: CustomPackageContainer { } } - public func getFileSystem() throws -> FileSystem? { + package func getFileSystem() throws -> FileSystem? { return fileSystem } - public convenience init( + package convenience init( name: String, dependenciesByVersion: [Version: [(container: String, versionRequirement: VersionSetSpecifier)]] ) throws { @@ -113,7 +113,7 @@ public class MockPackageContainer: CustomPackageContainer { self.init(package: ref, dependencies: dependencies) } - public init( + package init( package: PackageReference, dependencies: [String: [Dependency]] = [:], fileSystem: FileSystem? = nil, @@ -129,7 +129,7 @@ public class MockPackageContainer: CustomPackageContainer { self.customRetrievalPath = customRetrievalPath } - public init( + package init( name: String, dependenciesByProductFilter: [ProductFilter: [(container: String, versionRequirement: VersionSetSpecifier)]] ) throws { @@ -154,16 +154,16 @@ public class MockPackageContainer: CustomPackageContainer { } } -public struct MockPackageContainerProvider: PackageContainerProvider { - public let containers: [MockPackageContainer] - public let containersByIdentifier: [PackageReference: MockPackageContainer] +package struct MockPackageContainerProvider: PackageContainerProvider { + package let containers: [MockPackageContainer] + package let containersByIdentifier: [PackageReference: MockPackageContainer] - public init(containers: [MockPackageContainer]) { + package init(containers: [MockPackageContainer]) { self.containers = containers self.containersByIdentifier = Dictionary(uniqueKeysWithValues: containers.map { ($0.package, $0) }) } - public func getContainer( + package func getContainer( for package: PackageReference, updateStrategy: ContainerUpdateStrategy, observabilityScope: ObservabilityScope, diff --git a/Sources/SPMTestSupport/MockPackageFingerprintStorage.swift b/Sources/SPMTestSupport/MockPackageFingerprintStorage.swift index 2c331346ad5..fe08d4fc8d9 100644 --- a/Sources/SPMTestSupport/MockPackageFingerprintStorage.swift +++ b/Sources/SPMTestSupport/MockPackageFingerprintStorage.swift @@ -18,18 +18,18 @@ import PackageModel import struct TSCUtility.Version -public class MockPackageFingerprintStorage: PackageFingerprintStorage { +package class MockPackageFingerprintStorage: PackageFingerprintStorage { private var packageFingerprints: [PackageIdentity: [Version: [Fingerprint .Kind: [Fingerprint.ContentType: Fingerprint]]]] private let lock = NSLock() - public init(_ packageFingerprints: [PackageIdentity: [Version: [Fingerprint + package init(_ packageFingerprints: [PackageIdentity: [Version: [Fingerprint .Kind: [Fingerprint.ContentType: Fingerprint]]]] = [:]) { self.packageFingerprints = packageFingerprints } - public func get( + package func get( package: PackageIdentity, version: Version, observabilityScope: ObservabilityScope, @@ -47,7 +47,7 @@ public class MockPackageFingerprintStorage: PackageFingerprintStorage { } } - public func put( + package func put( package: PackageIdentity, version: Version, fingerprint: Fingerprint, @@ -86,7 +86,7 @@ public class MockPackageFingerprintStorage: PackageFingerprintStorage { } } - public func get( + package func get( package: PackageReference, version: Version, observabilityScope: ObservabilityScope, @@ -102,7 +102,7 @@ public class MockPackageFingerprintStorage: PackageFingerprintStorage { ) } - public func put( + package func put( package: PackageReference, version: Version, fingerprint: Fingerprint, diff --git a/Sources/SPMTestSupport/MockPackageGraphs.swift b/Sources/SPMTestSupport/MockPackageGraphs.swift index 4ab9c0cde98..3f7b77ec353 100644 --- a/Sources/SPMTestSupport/MockPackageGraphs.swift +++ b/Sources/SPMTestSupport/MockPackageGraphs.swift @@ -20,15 +20,13 @@ import struct PackageModel.TargetDescription import protocol TSCBasic.FileSystem import class TSCBasic.InMemoryFileSystem -@_spi(SwiftPMInternal) -public typealias MockPackageGraph = ( +package typealias MockPackageGraph = ( graph: ModulesGraph, fileSystem: any FileSystem, observabilityScope: ObservabilityScope ) -@_spi(SwiftPMInternal) -public func macrosPackageGraph() throws -> MockPackageGraph { +package func macrosPackageGraph() throws -> MockPackageGraph { let fs = InMemoryFileSystem(emptyFiles: "/swift-firmware/Sources/Core/source.swift", "/swift-firmware/Sources/HAL/source.swift", @@ -126,8 +124,7 @@ public func macrosPackageGraph() throws -> MockPackageGraph { return (graph, fs, observability.topScope) } -@_spi(SwiftPMInternal) -public func trivialPackageGraph(pkgRootPath: AbsolutePath) throws -> MockPackageGraph { +package func trivialPackageGraph(pkgRootPath: AbsolutePath) throws -> MockPackageGraph { let fs = InMemoryFileSystem( emptyFiles: "/Pkg/Sources/app/main.swift", @@ -157,8 +154,7 @@ public func trivialPackageGraph(pkgRootPath: AbsolutePath) throws -> MockPackage return (graph, fs, observability.topScope) } -@_spi(SwiftPMInternal) -public func embeddedCxxInteropPackageGraph(pkgRootPath: AbsolutePath) throws -> MockPackageGraph { +package func embeddedCxxInteropPackageGraph(pkgRootPath: AbsolutePath) throws -> MockPackageGraph { let fs = InMemoryFileSystem( emptyFiles: "/Pkg/Sources/app/main.swift", diff --git a/Sources/SPMTestSupport/MockPackageSigningEntityStorage.swift b/Sources/SPMTestSupport/MockPackageSigningEntityStorage.swift index c53aff4014a..727ff1bf67a 100644 --- a/Sources/SPMTestSupport/MockPackageSigningEntityStorage.swift +++ b/Sources/SPMTestSupport/MockPackageSigningEntityStorage.swift @@ -18,15 +18,15 @@ import PackageSigning import struct TSCUtility.Version -public class MockPackageSigningEntityStorage: PackageSigningEntityStorage { +package class MockPackageSigningEntityStorage: PackageSigningEntityStorage { private var packageSigners: [PackageIdentity: PackageSigners] private let lock = NSLock() - public init(_ packageSigners: [PackageIdentity: PackageSigners] = [:]) { + package init(_ packageSigners: [PackageIdentity: PackageSigners] = [:]) { self.packageSigners = packageSigners } - public func get( + package func get( package: PackageIdentity, observabilityScope: ObservabilityScope, callbackQueue: DispatchQueue @@ -42,7 +42,7 @@ public class MockPackageSigningEntityStorage: PackageSigningEntityStorage { } @available(*, noasync, message: "Use the async alternative") - public func get( + package func get( package: PackageIdentity, observabilityScope: ObservabilityScope, callbackQueue: DispatchQueue, @@ -59,7 +59,7 @@ public class MockPackageSigningEntityStorage: PackageSigningEntityStorage { } } - public func put( + package func put( package: PackageIdentity, version: Version, signingEntity: SigningEntity, @@ -100,7 +100,7 @@ public class MockPackageSigningEntityStorage: PackageSigningEntityStorage { } } - public func add( + package func add( package: PackageIdentity, version: Version, signingEntity: SigningEntity, @@ -126,7 +126,7 @@ public class MockPackageSigningEntityStorage: PackageSigningEntityStorage { } } - public func changeSigningEntityFromVersion( + package func changeSigningEntityFromVersion( package: PackageIdentity, version: Version, signingEntity: SigningEntity, @@ -157,7 +157,7 @@ public class MockPackageSigningEntityStorage: PackageSigningEntityStorage { } } - public func changeSigningEntityForAllVersions( + package func changeSigningEntityForAllVersions( package: PackageIdentity, version: Version, signingEntity: SigningEntity, diff --git a/Sources/SPMTestSupport/MockProduct.swift b/Sources/SPMTestSupport/MockProduct.swift index 8e7659e8d54..b35c4fa9e4f 100644 --- a/Sources/SPMTestSupport/MockProduct.swift +++ b/Sources/SPMTestSupport/MockProduct.swift @@ -10,11 +10,11 @@ // //===----------------------------------------------------------------------===// -public struct MockProduct { - public let name: String - public let targets: [String] +package struct MockProduct { + package let name: String + package let targets: [String] - public init(name: String, targets: [String]) { + package init(name: String, targets: [String]) { self.name = name self.targets = targets } diff --git a/Sources/SPMTestSupport/MockRegistry.swift b/Sources/SPMTestSupport/MockRegistry.swift index 04b622864e9..c361afccfe7 100644 --- a/Sources/SPMTestSupport/MockRegistry.swift +++ b/Sources/SPMTestSupport/MockRegistry.swift @@ -23,12 +23,12 @@ import protocol TSCBasic.HashAlgorithm import struct TSCUtility.Version -public class MockRegistry { +package class MockRegistry { private let baseURL: URL private let fileSystem: FileSystem private let identityResolver: IdentityResolver private let checksumAlgorithm: HashAlgorithm - public var registryClient: RegistryClient! + package var registryClient: RegistryClient! private let jsonEncoder: JSONEncoder private var packageVersions = [PackageIdentity: [String: InMemoryRegistryPackageSource]]() @@ -36,7 +36,7 @@ public class MockRegistry { private var sourceControlURLs = [URL: PackageIdentity]() private let packagesLock = NSLock() - public init( + package init( filesystem: FileSystem, identityResolver: IdentityResolver, checksumAlgorithm: HashAlgorithm, @@ -74,7 +74,7 @@ public class MockRegistry { ) } - public func addPackage( + package func addPackage( identity: PackageIdentity, versions: [Version], sourceControlURLs: [URL]? = .none, @@ -88,7 +88,7 @@ public class MockRegistry { ) } - public func addPackage( + package func addPackage( identity: PackageIdentity, versions: [String], sourceControlURLs: [URL]? = .none, @@ -356,16 +356,16 @@ public class MockRegistry { } } -public struct InMemoryRegistryPackageSource { +package struct InMemoryRegistryPackageSource { let fileSystem: FileSystem - public let path: AbsolutePath + package let path: AbsolutePath - public init(fileSystem: FileSystem, path: AbsolutePath, writeContent: Bool = true) { + package init(fileSystem: FileSystem, path: AbsolutePath, writeContent: Bool = true) { self.fileSystem = fileSystem self.path = path } - public func writePackageContent(targets: [String] = [], toolsVersion: ToolsVersion = .current) throws { + package func writePackageContent(targets: [String] = [], toolsVersion: ToolsVersion = .current) throws { try self.fileSystem.createDirectory(self.path, recursive: true) let sourcesDir = self.path.appending("Sources") for target in targets { @@ -377,7 +377,7 @@ public struct InMemoryRegistryPackageSource { try self.fileSystem.writeFileContents(manifestPath, string: "// swift-tools-version:\(toolsVersion)") } - public func listFiles(root: AbsolutePath? = .none) throws -> [AbsolutePath] { + package func listFiles(root: AbsolutePath? = .none) throws -> [AbsolutePath] { var files = [AbsolutePath]() let root = root ?? self.path let entries = try self.fileSystem.getDirectoryContents(root) @@ -455,7 +455,7 @@ private struct MockRegistryArchiver: Archiver { } extension RegistryConfiguration.Security { - public static let testDefault: RegistryConfiguration.Security = { + package static let testDefault: RegistryConfiguration.Security = { var signing = RegistryConfiguration.Security.Signing() signing.onUnsigned = .silentAllow signing.onUntrustedCertificate = .silentAllow diff --git a/Sources/SPMTestSupport/MockTarget.swift b/Sources/SPMTestSupport/MockTarget.swift index b2f065a81cb..2d9750d9d27 100644 --- a/Sources/SPMTestSupport/MockTarget.swift +++ b/Sources/SPMTestSupport/MockTarget.swift @@ -13,21 +13,21 @@ import PackageGraph import PackageModel -public struct MockTarget { - public enum `Type` { +package struct MockTarget { + package enum `Type` { case regular, test, binary } - public let name: String - public let dependencies: [TargetDescription.Dependency] - public let path: String? - public let url: String? - public let checksum: String? - public let packageAccess: Bool - public let settings: [TargetBuildSettingDescription.Setting] - public let type: Type + package let name: String + package let dependencies: [TargetDescription.Dependency] + package let path: String? + package let url: String? + package let checksum: String? + package let packageAccess: Bool + package let settings: [TargetBuildSettingDescription.Setting] + package let type: Type - public init( + package init( name: String, dependencies: [TargetDescription.Dependency] = [], type: Type = .regular, diff --git a/Sources/SPMTestSupport/MockWorkspace.swift b/Sources/SPMTestSupport/MockWorkspace.swift index 344034f4494..717fc4f50ee 100644 --- a/Sources/SPMTestSupport/MockWorkspace.swift +++ b/Sources/SPMTestSupport/MockWorkspace.swift @@ -23,7 +23,7 @@ import class TSCBasic.InMemoryFileSystem import struct TSCUtility.Version -public final class MockWorkspace { +package final class MockWorkspace { let sandbox: AbsolutePath let fileSystem: InMemoryFileSystem let roots: [MockPackage] @@ -32,20 +32,20 @@ public final class MockWorkspace { let fingerprints: MockPackageFingerprintStorage let signingEntities: MockPackageSigningEntityStorage let mirrors: DependencyMirrors - public var registryClient: RegistryClient + package var registryClient: RegistryClient let registry: MockRegistry let customBinaryArtifactsManager: Workspace.CustomBinaryArtifactsManager - public var checksumAlgorithm: MockHashAlgorithm - public private(set) var manifestLoader: MockManifestLoader - public let repositoryProvider: InMemoryGitRepositoryProvider + package var checksumAlgorithm: MockHashAlgorithm + package private(set) var manifestLoader: MockManifestLoader + package let repositoryProvider: InMemoryGitRepositoryProvider let identityResolver: IdentityResolver let customPackageContainerProvider: MockPackageContainerProvider? - public let delegate = MockWorkspaceDelegate() + package let delegate = MockWorkspaceDelegate() let skipDependenciesUpdates: Bool - public var sourceControlToRegistryDependencyTransformation: WorkspaceConfiguration.SourceControlToRegistryDependencyTransformation + package var sourceControlToRegistryDependencyTransformation: WorkspaceConfiguration.SourceControlToRegistryDependencyTransformation var defaultRegistry: Registry? - public init( + package init( sandbox: AbsolutePath, fileSystem: InMemoryFileSystem, roots: [MockPackage], @@ -96,23 +96,23 @@ public final class MockWorkspace { try self.create() } - public var rootsDir: AbsolutePath { + package var rootsDir: AbsolutePath { return self.sandbox.appending("roots") } - public var packagesDir: AbsolutePath { + package var packagesDir: AbsolutePath { return self.sandbox.appending("pkgs") } - public var artifactsDir: AbsolutePath { + package var artifactsDir: AbsolutePath { return self.sandbox.appending(components: ".build", "artifacts") } - public func pathToRoot(withName name: String) throws -> AbsolutePath { + package func pathToRoot(withName name: String) throws -> AbsolutePath { return try AbsolutePath(validating: name, relativeTo: self.rootsDir) } - public func pathToPackage(withName name: String) throws -> AbsolutePath { + package func pathToPackage(withName name: String) throws -> AbsolutePath { return try AbsolutePath(validating: name, relativeTo: self.packagesDir) } @@ -267,7 +267,7 @@ public final class MockWorkspace { self.manifestLoader = MockManifestLoader(manifests: manifests) } - public func getOrCreateWorkspace() throws -> Workspace { + package func getOrCreateWorkspace() throws -> Workspace { if let workspace = self._workspace { return workspace } @@ -317,7 +317,7 @@ public final class MockWorkspace { private var _workspace: Workspace? - public func closeWorkspace(resetState: Bool = true, resetResolvedFile: Bool = true) throws { + package func closeWorkspace(resetState: Bool = true, resetResolvedFile: Bool = true) throws { if resetState { try self._workspace?.resetState() } @@ -329,11 +329,11 @@ public final class MockWorkspace { self._workspace = nil } - public func rootPaths(for packages: [String]) throws -> [AbsolutePath] { + package func rootPaths(for packages: [String]) throws -> [AbsolutePath] { return try packages.map { try AbsolutePath(validating: $0, relativeTo: rootsDir) } } - public func checkEdit( + package func checkEdit( packageName: String, path: AbsolutePath? = nil, revision: Revision? = nil, @@ -354,7 +354,7 @@ public final class MockWorkspace { result(observability.diagnostics) } - public func checkUnedit( + package func checkUnedit( packageName: String, roots: [String], forceRemove: Bool = false, @@ -375,7 +375,7 @@ public final class MockWorkspace { result(observability.diagnostics) } - public func checkResolve(pkg: String, roots: [String], version: TSCUtility.Version, _ result: ([Basics.Diagnostic]) -> Void) { + package func checkResolve(pkg: String, roots: [String], version: TSCUtility.Version, _ result: ([Basics.Diagnostic]) -> Void) { let observability = ObservabilitySystem.makeForTesting() observability.topScope.trap { let rootInput = PackageGraphRootInput(packages: try rootPaths(for: roots)) @@ -385,7 +385,7 @@ public final class MockWorkspace { result(observability.diagnostics) } - public func checkClean(_ result: ([Basics.Diagnostic]) -> Void) { + package func checkClean(_ result: ([Basics.Diagnostic]) -> Void) { let observability = ObservabilitySystem.makeForTesting() observability.topScope.trap { let workspace = try self.getOrCreateWorkspace() @@ -394,7 +394,7 @@ public final class MockWorkspace { result(observability.diagnostics) } - public func checkReset(_ result: ([Basics.Diagnostic]) -> Void) { + package func checkReset(_ result: ([Basics.Diagnostic]) -> Void) { let observability = ObservabilitySystem.makeForTesting() observability.topScope.trap { let workspace = try self.getOrCreateWorkspace() @@ -403,7 +403,7 @@ public final class MockWorkspace { result(observability.diagnostics) } - public func checkUpdate( + package func checkUpdate( roots: [String] = [], deps: [MockDependency] = [], packages: [String] = [], @@ -423,7 +423,7 @@ public final class MockWorkspace { result(observability.diagnostics) } - public func checkUpdateDryRun( + package func checkUpdateDryRun( roots: [String] = [], deps: [MockDependency] = [], _ result: ([(PackageReference, Workspace.PackageStateChange)]?, [Basics.Diagnostic]) -> Void @@ -442,7 +442,7 @@ public final class MockWorkspace { result(changes, observability.diagnostics) } - public func checkPackageGraph( + package func checkPackageGraph( roots: [String] = [], deps: [MockDependency], _ result: (ModulesGraph, [Basics.Diagnostic]) -> Void @@ -451,7 +451,7 @@ public final class MockWorkspace { try self.checkPackageGraph(roots: roots, dependencies: dependencies, result) } - public func checkPackageGraph( + package func checkPackageGraph( roots: [String] = [], dependencies: [PackageDependency] = [], forceResolvedVersions: Bool = false, @@ -481,7 +481,7 @@ public final class MockWorkspace { } } - public func checkPackageGraphFailure( + package func checkPackageGraphFailure( roots: [String] = [], deps: [MockDependency], _ result: ([Basics.Diagnostic]) -> Void @@ -490,7 +490,7 @@ public final class MockWorkspace { self.checkPackageGraphFailure(roots: roots, dependencies: dependencies, result) } - public func checkPackageGraphFailure( + package func checkPackageGraphFailure( roots: [String] = [], dependencies: [PackageDependency] = [], forceResolvedVersions: Bool = false, @@ -513,12 +513,12 @@ public final class MockWorkspace { result(observability.diagnostics) } - public struct ResolutionPrecomputationResult { - public let result: Workspace.ResolutionPrecomputationResult - public let diagnostics: [Basics.Diagnostic] + package struct ResolutionPrecomputationResult { + package let result: Workspace.ResolutionPrecomputationResult + package let diagnostics: [Basics.Diagnostic] } - public func checkPrecomputeResolution() async throws -> ResolutionPrecomputationResult { + package func checkPrecomputeResolution() async throws -> ResolutionPrecomputationResult { let observability = ObservabilitySystem.makeForTesting() let workspace = try self.getOrCreateWorkspace() let pinsStore = try workspace.pinsStore.load() @@ -548,7 +548,7 @@ public final class MockWorkspace { return ResolutionPrecomputationResult(result: result, diagnostics: observability.diagnostics) } - public func set( + package func set( pins: [PackageReference: CheckoutState] = [:], managedDependencies: [AbsolutePath: Workspace.ManagedDependency] = [:], managedArtifacts: [Workspace.ManagedArtifact] = [] @@ -566,7 +566,7 @@ public final class MockWorkspace { try self.set(pins: pins, managedDependencies: managedDependencies, managedArtifacts: managedArtifacts) } - public func set( + package func set( pins: [PackageReference: PinsStore.PinState], managedDependencies: [AbsolutePath: Workspace.ManagedDependency] = [:], managedArtifacts: [Workspace.ManagedArtifact] = [] @@ -599,13 +599,13 @@ public final class MockWorkspace { try workspace.state.save() } - public func resetState() throws { + package func resetState() throws { let workspace = try self.getOrCreateWorkspace() try workspace.resetState() } - public enum State { - public enum CheckoutState { + package enum State { + package enum CheckoutState { case version(TSCUtility.Version) case revision(String) case branch(String) @@ -618,31 +618,31 @@ public final class MockWorkspace { case custom(TSCUtility.Version, AbsolutePath) } - public struct ManagedDependencyResult { - public let managedDependencies: Workspace.ManagedDependencies + package struct ManagedDependencyResult { + package let managedDependencies: Workspace.ManagedDependencies - public init(_ managedDependencies: Workspace.ManagedDependencies) { + package init(_ managedDependencies: Workspace.ManagedDependencies) { self.managedDependencies = managedDependencies } - public func check(notPresent name: String, file: StaticString = #file, line: UInt = #line) { + package func check(notPresent name: String, file: StaticString = #file, line: UInt = #line) { self.check(notPresent: .plain(name), file: file, line: line) } - public func check(notPresent dependencyId: PackageIdentity, file: StaticString = #file, line: UInt = #line) { + package func check(notPresent dependencyId: PackageIdentity, file: StaticString = #file, line: UInt = #line) { let dependency = self.managedDependencies[dependencyId] XCTAssertNil(dependency, "Unexpectedly found \(dependencyId) in managed dependencies", file: file, line: line) } - public func checkEmpty(file: StaticString = #file, line: UInt = #line) { + package func checkEmpty(file: StaticString = #file, line: UInt = #line) { XCTAssertEqual(self.managedDependencies.count, 0, file: file, line: line) } - public func check(dependency name: String, at state: State, file: StaticString = #file, line: UInt = #line) { + package func check(dependency name: String, at state: State, file: StaticString = #file, line: UInt = #line) { self.check(dependency: .plain(name), at: state, file: file, line: line) } - public func check(dependency dependencyId: PackageIdentity, at state: State, file: StaticString = #file, line: UInt = #line) { + package func check(dependency dependencyId: PackageIdentity, at state: State, file: StaticString = #file, line: UInt = #line) { guard let dependency = managedDependencies[dependencyId] else { return XCTFail("\(dependencyId) does not exists", file: file, line: line) } @@ -683,18 +683,18 @@ public final class MockWorkspace { } } - public struct ManagedArtifactResult { - public let managedArtifacts: Workspace.ManagedArtifacts + package struct ManagedArtifactResult { + package let managedArtifacts: Workspace.ManagedArtifacts - public init(_ managedArtifacts: Workspace.ManagedArtifacts) { + package init(_ managedArtifacts: Workspace.ManagedArtifacts) { self.managedArtifacts = managedArtifacts } - public func checkNotPresent(packageName: String, targetName: String, file: StaticString = #file, line: UInt = #line) { + package func checkNotPresent(packageName: String, targetName: String, file: StaticString = #file, line: UInt = #line) { self.checkNotPresent(packageIdentity: .plain(packageName), targetName: targetName, file : file, line: line) } - public func checkNotPresent( + package func checkNotPresent( packageIdentity: PackageIdentity, targetName: String, file: StaticString = #file, @@ -704,15 +704,15 @@ public final class MockWorkspace { XCTAssert(artifact == nil, "Unexpectedly found \(packageIdentity).\(targetName) in managed artifacts", file: file, line: line) } - public func checkEmpty(file: StaticString = #file, line: UInt = #line) { + package func checkEmpty(file: StaticString = #file, line: UInt = #line) { XCTAssertEqual(self.managedArtifacts.count, 0, file: file, line: line) } - public func check(packageName: String, targetName: String, source: Workspace.ManagedArtifact.Source, path: AbsolutePath, file: StaticString = #file, line: UInt = #line) { + package func check(packageName: String, targetName: String, source: Workspace.ManagedArtifact.Source, path: AbsolutePath, file: StaticString = #file, line: UInt = #line) { self.check(packageIdentity: .plain(packageName), targetName: targetName, source: source, path: path, file: file, line: line) } - public func check( + package func check( packageIdentity: PackageIdentity, targetName: String, source: Workspace.ManagedArtifact.Source, @@ -737,7 +737,7 @@ public final class MockWorkspace { } } - public func loadDependencyManifests( + package func loadDependencyManifests( roots: [String] = [], deps: [MockDependency] = [], _ result: (Workspace.DependencyManifests, [Basics.Diagnostic]) -> Void @@ -758,7 +758,7 @@ public final class MockWorkspace { result(manifests, observability.diagnostics) } - public func checkManagedDependencies(file: StaticString = #file, line: UInt = #line, _ result: (ManagedDependencyResult) throws -> Void) { + package func checkManagedDependencies(file: StaticString = #file, line: UInt = #line, _ result: (ManagedDependencyResult) throws -> Void) { do { let workspace = try self.getOrCreateWorkspace() try result(ManagedDependencyResult(workspace.state.dependencies)) @@ -767,7 +767,7 @@ public final class MockWorkspace { } } - public func checkManagedArtifacts(file: StaticString = #file, line: UInt = #line, _ result: (ManagedArtifactResult) throws -> Void) { + package func checkManagedArtifacts(file: StaticString = #file, line: UInt = #line, _ result: (ManagedArtifactResult) throws -> Void) { do { let workspace = try self.getOrCreateWorkspace() try result(ManagedArtifactResult(workspace.state.artifacts)) @@ -776,18 +776,18 @@ public final class MockWorkspace { } } - public struct ResolvedResult { - public let store: PinsStore + package struct ResolvedResult { + package let store: PinsStore - public init(_ store: PinsStore) { + package init(_ store: PinsStore) { self.store = store } - public func check(notPresent name: String, file: StaticString = #file, line: UInt = #line) { + package func check(notPresent name: String, file: StaticString = #file, line: UInt = #line) { XCTAssertFalse(self.store.pins.keys.contains(where: { $0.description == name }), "Unexpectedly found \(name) in Package.resolved", file: file, line: line) } - public func check(dependency package: String, at state: State, file: StaticString = #file, line: UInt = #line) { + package func check(dependency package: String, at state: State, file: StaticString = #file, line: UInt = #line) { guard let pin = store.pins.first(where: { $0.key.description == package })?.value else { XCTFail("Pin for \(package) not found", file: file, line: line) return @@ -814,7 +814,7 @@ public final class MockWorkspace { } } - public func check(dependency package: String, url: String, file: StaticString = #file, line: UInt = #line) { + package func check(dependency package: String, url: String, file: StaticString = #file, line: UInt = #line) { guard let pin = store.pins.first(where: { $0.key.description == package })?.value else { XCTFail("Pin for \(package) not found", file: file, line: line) return @@ -824,7 +824,7 @@ public final class MockWorkspace { } } - public func checkResolved(file: StaticString = #file, line: UInt = #line, _ result: (ResolvedResult) throws -> Void) { + package func checkResolved(file: StaticString = #file, line: UInt = #line, _ result: (ResolvedResult) throws -> Void) { do { let workspace = try self.getOrCreateWorkspace() try result(ResolvedResult(workspace.pinsStore.load())) @@ -834,66 +834,66 @@ public final class MockWorkspace { } } -public final class MockWorkspaceDelegate: WorkspaceDelegate { +package final class MockWorkspaceDelegate: WorkspaceDelegate { private let lock = NSLock() private var _events = [String]() private var _manifest: Manifest? private var _manifestLoadingDiagnostics: [Basics.Diagnostic]? - public init() {} + package init() {} - public func willUpdateRepository(package: PackageIdentity, repository url: String) { + package func willUpdateRepository(package: PackageIdentity, repository url: String) { self.append("updating repo: \(url)") } - public func didUpdateRepository(package: PackageIdentity, repository url: String, duration: DispatchTimeInterval) { + package func didUpdateRepository(package: PackageIdentity, repository url: String, duration: DispatchTimeInterval) { self.append("finished updating repo: \(url)") } - public func dependenciesUpToDate() { + package func dependenciesUpToDate() { self.append("Everything is already up-to-date") } - public func willFetchPackage(package: PackageIdentity, packageLocation: String?, fetchDetails: PackageFetchDetails) { + package func willFetchPackage(package: PackageIdentity, packageLocation: String?, fetchDetails: PackageFetchDetails) { self.append("fetching package: \(packageLocation ?? package.description)") } - public func fetchingPackage(package: PackageIdentity, packageLocation: String?, progress: Int64, total: Int64?) { + package func fetchingPackage(package: PackageIdentity, packageLocation: String?, progress: Int64, total: Int64?) { } - public func didFetchPackage(package: PackageIdentity, packageLocation: String?, result: Result, duration: DispatchTimeInterval) { + package func didFetchPackage(package: PackageIdentity, packageLocation: String?, result: Result, duration: DispatchTimeInterval) { self.append("finished fetching package: \(packageLocation ?? package.description)") } - public func willCreateWorkingCopy(package: PackageIdentity, repository url: String, at path: AbsolutePath) { + package func willCreateWorkingCopy(package: PackageIdentity, repository url: String, at path: AbsolutePath) { self.append("creating working copy for: \(url)") } - public func didCreateWorkingCopy(package: PackageIdentity, repository url: String, at path: AbsolutePath, duration: DispatchTimeInterval) { + package func didCreateWorkingCopy(package: PackageIdentity, repository url: String, at path: AbsolutePath, duration: DispatchTimeInterval) { self.append("finished creating working copy for: \(url)") } - public func willCheckOut(package: PackageIdentity, repository url: String, revision: String, at path: AbsolutePath) { + package func willCheckOut(package: PackageIdentity, repository url: String, revision: String, at path: AbsolutePath) { self.append("checking out repo: \(url)") } - public func didCheckOut(package: PackageIdentity, repository url: String, revision: String, at path: AbsolutePath, duration: DispatchTimeInterval) { + package func didCheckOut(package: PackageIdentity, repository url: String, revision: String, at path: AbsolutePath, duration: DispatchTimeInterval) { self.append("finished checking out repo: \(url)") } - public func removing(package: PackageIdentity, packageLocation: String?) { + package func removing(package: PackageIdentity, packageLocation: String?) { self.append("removing repo: \(packageLocation ?? package.description)") } - public func willResolveDependencies(reason: WorkspaceResolveReason) { + package func willResolveDependencies(reason: WorkspaceResolveReason) { self.append("will resolve dependencies") } - public func willLoadManifest(packageIdentity: PackageIdentity, packagePath: AbsolutePath, url: String, version: Version?, packageKind: PackageReference.Kind) { + package func willLoadManifest(packageIdentity: PackageIdentity, packagePath: AbsolutePath, url: String, version: Version?, packageKind: PackageReference.Kind) { self.append("will load manifest for \(packageKind.displayName) package: \(url) (identity: \(packageIdentity))") } - public func didLoadManifest(packageIdentity: PackageIdentity, packagePath: AbsolutePath, url: String, version: Version?, packageKind: PackageReference.Kind, manifest: Manifest?, diagnostics: [Basics.Diagnostic], duration: DispatchTimeInterval) { + package func didLoadManifest(packageIdentity: PackageIdentity, packagePath: AbsolutePath, url: String, version: Version?, packageKind: PackageReference.Kind, manifest: Manifest?, diagnostics: [Basics.Diagnostic], duration: DispatchTimeInterval) { self.append("did load manifest for \(packageKind.displayName) package: \(url) (identity: \(packageIdentity))") self.lock.withLock { self._manifest = manifest @@ -901,71 +901,71 @@ public final class MockWorkspaceDelegate: WorkspaceDelegate { } } - public func willComputeVersion(package: PackageIdentity, location: String) { + package func willComputeVersion(package: PackageIdentity, location: String) { // noop } - public func didComputeVersion(package: PackageIdentity, location: String, version: String, duration: DispatchTimeInterval) { + package func didComputeVersion(package: PackageIdentity, location: String, version: String, duration: DispatchTimeInterval) { // noop } - public func resolvedFileChanged() { + package func resolvedFileChanged() { // noop } - public func willDownloadBinaryArtifact(from url: String, fromCache: Bool) { + package func willDownloadBinaryArtifact(from url: String, fromCache: Bool) { self.append("downloading binary artifact package: \(url)") } - public func didDownloadBinaryArtifact(from url: String, result: Result<(path: AbsolutePath, fromCache: Bool), Error>, duration: DispatchTimeInterval) { + package func didDownloadBinaryArtifact(from url: String, result: Result<(path: AbsolutePath, fromCache: Bool), Error>, duration: DispatchTimeInterval) { self.append("finished downloading binary artifact package: \(url)") } - public func downloadingBinaryArtifact(from url: String, bytesDownloaded: Int64, totalBytesToDownload: Int64?) { + package func downloadingBinaryArtifact(from url: String, bytesDownloaded: Int64, totalBytesToDownload: Int64?) { // noop } - public func didDownloadAllBinaryArtifacts() { + package func didDownloadAllBinaryArtifacts() { // noop } - public func willUpdateDependencies() { + package func willUpdateDependencies() { // noop } - public func didUpdateDependencies(duration: DispatchTimeInterval) { + package func didUpdateDependencies(duration: DispatchTimeInterval) { // noop } - public func willResolveDependencies() { + package func willResolveDependencies() { // noop } - public func didResolveDependencies(duration: DispatchTimeInterval) { + package func didResolveDependencies(duration: DispatchTimeInterval) { // noop } - public func willLoadGraph() { + package func willLoadGraph() { // noop } - public func didLoadGraph(duration: DispatchTimeInterval) { + package func didLoadGraph(duration: DispatchTimeInterval) { // noop } - public func willCompileManifest(packageIdentity: PackageIdentity, packageLocation: String) { + package func willCompileManifest(packageIdentity: PackageIdentity, packageLocation: String) { // noop } - public func didCompileManifest(packageIdentity: PackageIdentity, packageLocation: String, duration: DispatchTimeInterval) { + package func didCompileManifest(packageIdentity: PackageIdentity, packageLocation: String, duration: DispatchTimeInterval) { // noop } - public func willEvaluateManifest(packageIdentity: PackageIdentity, packageLocation: String) { + package func willEvaluateManifest(packageIdentity: PackageIdentity, packageLocation: String) { // noop } - public func didEvaluateManifest(packageIdentity: PackageIdentity, packageLocation: String, duration: DispatchTimeInterval) { + package func didEvaluateManifest(packageIdentity: PackageIdentity, packageLocation: String, duration: DispatchTimeInterval) { // noop } @@ -975,25 +975,25 @@ public final class MockWorkspaceDelegate: WorkspaceDelegate { } } - public var events: [String] { + package var events: [String] { self.lock.withLock { self._events } } - public func clear() { + package func clear() { self.lock.withLock { self._events = [] } } - public var manifest: Manifest? { + package var manifest: Manifest? { self.lock.withLock { self._manifest } } - public var manifestLoadingDiagnostics: [Basics.Diagnostic]? { + package var manifestLoadingDiagnostics: [Basics.Diagnostic]? { self.lock.withLock { self._manifestLoadingDiagnostics } @@ -1001,7 +1001,7 @@ public final class MockWorkspaceDelegate: WorkspaceDelegate { } extension CheckoutState { - public var version: Version? { + package var version: Version? { get { switch self { case .revision: @@ -1014,7 +1014,7 @@ extension CheckoutState { } } - public var branch: String? { + package var branch: String? { get { switch self { case .revision: @@ -1061,11 +1061,11 @@ extension CheckoutState { } extension Array where Element == Basics.Diagnostic { - public var hasErrors: Bool { + package var hasErrors: Bool { self.contains(where: { $0.severity == .error }) } - public var hasWarnings: Bool { + package var hasWarnings: Bool { self.contains(where: { $0.severity == .warning }) } } diff --git a/Sources/SPMTestSupport/Observability.swift b/Sources/SPMTestSupport/Observability.swift index 198e5b898f2..f997150e61a 100644 --- a/Sources/SPMTestSupport/Observability.swift +++ b/Sources/SPMTestSupport/Observability.swift @@ -19,43 +19,43 @@ import struct TSCBasic.StringError import TSCTestSupport extension ObservabilitySystem { - public static func makeForTesting(verbose: Bool = true) -> TestingObservability { + package static func makeForTesting(verbose: Bool = true) -> TestingObservability { let collector = TestingObservability.Collector(verbose: verbose) let observabilitySystem = ObservabilitySystem(collector) return TestingObservability(collector: collector, topScope: observabilitySystem.topScope) } - public static var NOOP: ObservabilityScope { + package static var NOOP: ObservabilityScope { ObservabilitySystem { _, _ in }.topScope } } -public struct TestingObservability { +package struct TestingObservability { private let collector: Collector - public let topScope: ObservabilityScope + package let topScope: ObservabilityScope fileprivate init(collector: Collector, topScope: ObservabilityScope) { self.collector = collector self.topScope = topScope } - public var diagnostics: [Basics.Diagnostic] { + package var diagnostics: [Basics.Diagnostic] { self.collector.diagnostics.get() } - public var errors: [Basics.Diagnostic] { + package var errors: [Basics.Diagnostic] { self.diagnostics.filter { $0.severity == .error } } - public var warnings: [Basics.Diagnostic] { + package var warnings: [Basics.Diagnostic] { self.diagnostics.filter { $0.severity == .warning } } - public var hasErrorDiagnostics: Bool { + package var hasErrorDiagnostics: Bool { self.collector.hasErrors } - public var hasWarningDiagnostics: Bool { + package var hasWarningDiagnostics: Bool { self.collector.hasWarnings } @@ -93,7 +93,7 @@ public struct TestingObservability { } } -public func XCTAssertNoDiagnostics( +package func XCTAssertNoDiagnostics( _ diagnostics: [Basics.Diagnostic], problemsOnly: Bool = true, file: StaticString = #file, @@ -105,7 +105,7 @@ public func XCTAssertNoDiagnostics( XCTFail("Found unexpected diagnostics: \n\(description)", file: file, line: line) } -public func testDiagnostics( +package func testDiagnostics( _ diagnostics: [Basics.Diagnostic], problemsOnly: Bool = true, file: StaticString = #file, @@ -121,7 +121,7 @@ public func testDiagnostics( ) } -public func testDiagnostics( +package func testDiagnostics( _ diagnostics: [Basics.Diagnostic], minSeverity: Basics.Diagnostic.Severity, file: StaticString = #file, @@ -142,7 +142,7 @@ public func testDiagnostics( } } -public func testPartialDiagnostics( +package func testPartialDiagnostics( _ diagnostics: [Basics.Diagnostic], minSeverity: Basics.Diagnostic.Severity, file: StaticString = #file, @@ -160,7 +160,7 @@ public func testPartialDiagnostics( } /// Helper to check diagnostics in the engine. -public class DiagnosticsTestResult { +package class DiagnosticsTestResult { fileprivate var uncheckedDiagnostics: [Basics.Diagnostic] init(_ diagnostics: [Basics.Diagnostic]) { @@ -168,7 +168,7 @@ public class DiagnosticsTestResult { } @discardableResult - public func check( + package func check( diagnostic message: StringPattern, severity: Basics.Diagnostic.Severity, //metadata: ObservabilityMetadata? = .none, @@ -192,7 +192,7 @@ public class DiagnosticsTestResult { } @discardableResult - public func checkUnordered( + package func checkUnordered( diagnostic diagnosticPattern: StringPattern, severity: Basics.Diagnostic.Severity, //metadata: ObservabilityMetadata? = .none, diff --git a/Sources/SPMTestSupport/PIFTester.swift b/Sources/SPMTestSupport/PIFTester.swift index 85b64cbf6e5..b03b1a4e827 100644 --- a/Sources/SPMTestSupport/PIFTester.swift +++ b/Sources/SPMTestSupport/PIFTester.swift @@ -14,11 +14,11 @@ import Basics import XCBuildSupport import XCTest -public func PIFTester(_ pif: PIF.TopLevelObject, _ body: (PIFWorkspaceTester) throws -> Void) throws { +package func PIFTester(_ pif: PIF.TopLevelObject, _ body: (PIFWorkspaceTester) throws -> Void) throws { try body(PIFWorkspaceTester(workspace: pif.workspace)) } -public final class PIFWorkspaceTester { +package final class PIFWorkspaceTester { private let workspace: PIF.Workspace private let projectMap: [PIF.GUID: PIF.Project] private let targetMap: [PIF.GUID: PIF.BaseTarget] @@ -32,7 +32,7 @@ public final class PIFWorkspaceTester { targetMap = Dictionary(uniqueKeysWithValues: targetsByGUID) } - public func checkProject( + package func checkProject( _ guid: PIF.GUID, file: StaticString = #file, line: UInt = #line, @@ -46,16 +46,16 @@ public final class PIFWorkspaceTester { } } -public final class PIFProjectTester { +package final class PIFProjectTester { private let project: PIF.Project private let targetMap: [PIF.GUID: PIF.BaseTarget] private let fileMap: [PIF.GUID: String] - public var guid: PIF.GUID { project.guid } - public var path: AbsolutePath { project.path } - public var projectDirectory: AbsolutePath { project.projectDirectory } - public var name: String { project.name } - public var developmentRegion: String { project.developmentRegion } + package var guid: PIF.GUID { project.guid } + package var path: AbsolutePath { project.path } + package var projectDirectory: AbsolutePath { project.projectDirectory } + package var name: String { project.name } + package var developmentRegion: String { project.developmentRegion } fileprivate init(project: PIF.Project, targetMap: [PIF.GUID: PIF.BaseTarget]) throws { self.project = project @@ -68,7 +68,7 @@ public final class PIFProjectTester { ) } - public func checkTarget( + package func checkTarget( _ guid: PIF.GUID, file: StaticString = #file, line: UInt = #line, @@ -86,7 +86,7 @@ public final class PIFProjectTester { body?(PIFTargetTester(target: target, targetMap: targetMap, fileMap: fileMap)) } - public func checkNoTarget( + package func checkNoTarget( _ guid: PIF.GUID, file: StaticString = #file, line: UInt = #line, @@ -97,7 +97,7 @@ public final class PIFProjectTester { } } - public func checkAggregateTarget( + package func checkAggregateTarget( _ guid: PIF.GUID, file: StaticString = #file, line: UInt = #line, @@ -115,7 +115,7 @@ public final class PIFProjectTester { body?(PIFAggregateTargetTester(target: target, targetMap: targetMap, fileMap: fileMap)) } - public func checkBuildConfiguration( + package func checkBuildConfiguration( _ name: String, file: StaticString = #file, line: UInt = #line, @@ -129,24 +129,24 @@ public final class PIFProjectTester { body(PIFBuildConfigurationTester(buildConfiguration: configuration)) } - public func buildConfiguration(withName name: String) -> PIF.BuildConfiguration? { + package func buildConfiguration(withName name: String) -> PIF.BuildConfiguration? { return project.buildConfigurations.first { $0.name == name } } - public func baseTarget(withGUID guid: PIF.GUID) -> PIF.BaseTarget? { + package func baseTarget(withGUID guid: PIF.GUID) -> PIF.BaseTarget? { return project.targets.first { $0.guid == guid } } } -public class PIFBaseTargetTester { - public let baseTarget: PIF.BaseTarget +package class PIFBaseTargetTester { + package let baseTarget: PIF.BaseTarget - public var guid: PIF.GUID { baseTarget.guid } - public var name: String { baseTarget.name } - public let dependencies: Set - public let sources: Set - public let frameworks: Set - public let resources: Set + package var guid: PIF.GUID { baseTarget.guid } + package var name: String { baseTarget.name } + package let dependencies: Set + package let sources: Set + package let frameworks: Set + package let resources: Set fileprivate init(baseTarget: PIF.BaseTarget, targetMap: [PIF.GUID: PIF.BaseTarget], fileMap: [PIF.GUID: String]) { self.baseTarget = baseTarget @@ -181,7 +181,7 @@ public class PIFBaseTargetTester { }) } - public func checkBuildConfiguration( + package func checkBuildConfiguration( _ name: String, file: StaticString = #file, line: UInt = #line, @@ -194,11 +194,11 @@ public class PIFBaseTargetTester { body(PIFBuildConfigurationTester(buildConfiguration: configuration)) } - public func buildConfiguration(withName name: String) -> PIF.BuildConfiguration? { + package func buildConfiguration(withName name: String) -> PIF.BuildConfiguration? { return baseTarget.buildConfigurations.first { $0.name == name } } - public func checkImpartedBuildSettings( + package func checkImpartedBuildSettings( file: StaticString = #file, line: UInt = #line, _ body: (PIFBuildSettingsTester) -> Void @@ -209,7 +209,7 @@ public class PIFBaseTargetTester { body(buildSettingsTester) } - public func checkAllImpartedBuildSettings( + package func checkAllImpartedBuildSettings( file: StaticString = #file, line: UInt = #line, _ body: (PIFBuildSettingsTester) -> Void @@ -221,7 +221,7 @@ public class PIFBaseTargetTester { buildSettingsTester.checkUncheckedSettings(file: file, line: line) } - public func checkNoImpartedBuildSettings(file: StaticString = #file, line: UInt = #line) { + package func checkNoImpartedBuildSettings(file: StaticString = #file, line: UInt = #line) { let buildSettingsTester = PIFBuildSettingsTester( buildSettings: baseTarget.buildConfigurations.first!.impartedBuildProperties.buildSettings ) @@ -229,10 +229,10 @@ public class PIFBaseTargetTester { } } -public final class PIFTargetTester: PIFBaseTargetTester { +package final class PIFTargetTester: PIFBaseTargetTester { private let target: PIF.Target - public var productType: PIF.Target.ProductType { target.productType } - public var productName: String { target.productName } + package var productType: PIF.Target.ProductType { target.productType } + package var productName: String { target.productName } fileprivate init(target: PIF.Target, targetMap: [PIF.GUID: PIF.BaseTarget], fileMap: [PIF.GUID: String]) { self.target = target @@ -240,7 +240,7 @@ public final class PIFTargetTester: PIFBaseTargetTester { } } -public final class PIFAggregateTargetTester: PIFBaseTargetTester { +package final class PIFAggregateTargetTester: PIFBaseTargetTester { private let target: PIF.AggregateTarget fileprivate init(target: PIF.AggregateTarget, targetMap: [PIF.GUID: PIF.BaseTarget], fileMap: [PIF.GUID: String]) { @@ -249,41 +249,41 @@ public final class PIFAggregateTargetTester: PIFBaseTargetTester { } } -public final class PIFBuildConfigurationTester { +package final class PIFBuildConfigurationTester { private let buildConfiguration: PIF.BuildConfiguration - public var guid: PIF.GUID { buildConfiguration.guid } - public var name: String { buildConfiguration.name } + package var guid: PIF.GUID { buildConfiguration.guid } + package var name: String { buildConfiguration.name } fileprivate init(buildConfiguration: PIF.BuildConfiguration) { self.buildConfiguration = buildConfiguration } - public func checkBuildSettings(file: StaticString = #file, line: UInt = #line, _ body: (PIFBuildSettingsTester) -> Void) { + package func checkBuildSettings(file: StaticString = #file, line: UInt = #line, _ body: (PIFBuildSettingsTester) -> Void) { let buildSettingsTester = PIFBuildSettingsTester(buildSettings: buildConfiguration.buildSettings) body(buildSettingsTester) } - public func checkAllBuildSettings(file: StaticString = #file, line: UInt = #line, _ body: (PIFBuildSettingsTester) -> Void) { + package func checkAllBuildSettings(file: StaticString = #file, line: UInt = #line, _ body: (PIFBuildSettingsTester) -> Void) { let buildSettingsTester = PIFBuildSettingsTester(buildSettings: buildConfiguration.buildSettings) body(buildSettingsTester) buildSettingsTester.checkUncheckedSettings(file: file, line: line) } - public func checkNoBuildSettings(file: StaticString = #file, line: UInt = #line) { + package func checkNoBuildSettings(file: StaticString = #file, line: UInt = #line) { let buildSettingsTester = PIFBuildSettingsTester(buildSettings: buildConfiguration.buildSettings) buildSettingsTester.checkUncheckedSettings(file: file, line: line) } } -public final class PIFBuildSettingsTester { +package final class PIFBuildSettingsTester { private var buildSettings: PIF.BuildSettings fileprivate init(buildSettings: PIF.BuildSettings) { self.buildSettings = buildSettings } - public subscript(_ key: PIF.BuildSettings.SingleValueSetting) -> String? { + package subscript(_ key: PIF.BuildSettings.SingleValueSetting) -> String? { if let value = buildSettings[key] { buildSettings[key] = nil return value @@ -292,7 +292,7 @@ public final class PIFBuildSettingsTester { } } - public subscript(_ key: PIF.BuildSettings.SingleValueSetting, for platform: PIF.BuildSettings.Platform) -> String? { + package subscript(_ key: PIF.BuildSettings.SingleValueSetting, for platform: PIF.BuildSettings.Platform) -> String? { if let value = buildSettings[key, for: platform] { buildSettings[key, for: platform] = nil return value @@ -301,7 +301,7 @@ public final class PIFBuildSettingsTester { } } - public subscript(_ key: PIF.BuildSettings.MultipleValueSetting) -> [String]? { + package subscript(_ key: PIF.BuildSettings.MultipleValueSetting) -> [String]? { if let value = buildSettings[key] { buildSettings[key] = nil return value @@ -310,7 +310,7 @@ public final class PIFBuildSettingsTester { } } - public subscript(_ key: PIF.BuildSettings.MultipleValueSetting, for platform: PIF.BuildSettings.Platform) -> [String]? { + package subscript(_ key: PIF.BuildSettings.MultipleValueSetting, for platform: PIF.BuildSettings.Platform) -> [String]? { if let value = buildSettings[key, for: platform] { buildSettings[key, for: platform] = nil return value @@ -319,7 +319,7 @@ public final class PIFBuildSettingsTester { } } - public func checkUncheckedSettings(file: StaticString = #file, line: UInt = #line) { + package func checkUncheckedSettings(file: StaticString = #file, line: UInt = #line) { let uncheckedKeys = Array(buildSettings.singleValueSettings.keys.map { $0.rawValue }) + Array(buildSettings.multipleValueSettings.keys.map { $0.rawValue }) diff --git a/Sources/SPMTestSupport/PackageDependencyDescriptionExtensions.swift b/Sources/SPMTestSupport/PackageDependencyDescriptionExtensions.swift index 3a167448a6f..16c289dc583 100644 --- a/Sources/SPMTestSupport/PackageDependencyDescriptionExtensions.swift +++ b/Sources/SPMTestSupport/PackageDependencyDescriptionExtensions.swift @@ -16,7 +16,7 @@ import PackageModel import struct TSCUtility.Version -public extension PackageDependency { +package extension PackageDependency { static func fileSystem(identity: PackageIdentity? = nil, deprecatedName: String? = nil, path: AbsolutePath, @@ -70,19 +70,19 @@ public extension PackageDependency { // backwards compatibility with existing tests extension PackageDependency.SourceControl.Requirement { - public static func upToNextMajor(from version: Version) -> Self { + package static func upToNextMajor(from version: Version) -> Self { return .range(.upToNextMajor(from: version)) } - public static func upToNextMinor(from version: Version) -> Self { + package static func upToNextMinor(from version: Version) -> Self { return .range(.upToNextMinor(from: version)) } } extension PackageDependency.Registry.Requirement { - public static func upToNextMajor(from version: Version) -> Self { + package static func upToNextMajor(from version: Version) -> Self { return .range(.upToNextMajor(from: version)) } - public static func upToNextMinor(from version: Version) -> Self { + package static func upToNextMinor(from version: Version) -> Self { return .range(.upToNextMinor(from: version)) } } diff --git a/Sources/SPMTestSupport/PackageGraphTester.swift b/Sources/SPMTestSupport/PackageGraphTester.swift index 97aa593a971..7a40c5a6d3f 100644 --- a/Sources/SPMTestSupport/PackageGraphTester.swift +++ b/Sources/SPMTestSupport/PackageGraphTester.swift @@ -17,36 +17,36 @@ import struct Basics.IdentifiableSet import PackageModel import PackageGraph -public func PackageGraphTester(_ graph: ModulesGraph, _ result: (PackageGraphResult) -> Void) { +package func PackageGraphTester(_ graph: ModulesGraph, _ result: (PackageGraphResult) -> Void) { result(PackageGraphResult(graph)) } -public final class PackageGraphResult { - public let graph: ModulesGraph +package final class PackageGraphResult { + package let graph: ModulesGraph - public init(_ graph: ModulesGraph) { + package init(_ graph: ModulesGraph) { self.graph = graph } // TODO: deprecate / transition to PackageIdentity - public func check(roots: String..., file: StaticString = #file, line: UInt = #line) { + package func check(roots: String..., file: StaticString = #file, line: UInt = #line) { XCTAssertEqual(graph.rootPackages.map{$0.manifest.displayName }.sorted(), roots.sorted(), file: file, line: line) } - public func check(roots: PackageIdentity..., file: StaticString = #file, line: UInt = #line) { + package func check(roots: PackageIdentity..., file: StaticString = #file, line: UInt = #line) { XCTAssertEqual(graph.rootPackages.map{$0.identity }.sorted(), roots.sorted(), file: file, line: line) } // TODO: deprecate / transition to PackageIdentity - public func check(packages: String..., file: StaticString = #file, line: UInt = #line) { + package func check(packages: String..., file: StaticString = #file, line: UInt = #line) { XCTAssertEqual(graph.packages.map {$0.manifest.displayName }.sorted(), packages.sorted(), file: file, line: line) } - public func check(packages: PackageIdentity..., file: StaticString = #file, line: UInt = #line) { + package func check(packages: PackageIdentity..., file: StaticString = #file, line: UInt = #line) { XCTAssertEqual(graph.packages.map {$0.identity }.sorted(), packages.sorted(), file: file, line: line) } - public func check(targets: String..., file: StaticString = #file, line: UInt = #line) { + package func check(targets: String..., file: StaticString = #file, line: UInt = #line) { XCTAssertEqual( graph.allTargets .filter{ $0.type != .test } @@ -54,19 +54,19 @@ public final class PackageGraphResult { .sorted(), targets.sorted(), file: file, line: line) } - public func check(products: String..., file: StaticString = #file, line: UInt = #line) { + package func check(products: String..., file: StaticString = #file, line: UInt = #line) { XCTAssertEqual(Set(graph.allProducts.map { $0.name }), Set(products), file: file, line: line) } - public func check(reachableTargets: String..., file: StaticString = #file, line: UInt = #line) { + package func check(reachableTargets: String..., file: StaticString = #file, line: UInt = #line) { XCTAssertEqual(Set(graph.reachableTargets.map { $0.name }), Set(reachableTargets), file: file, line: line) } - public func check(reachableProducts: String..., file: StaticString = #file, line: UInt = #line) { + package func check(reachableProducts: String..., file: StaticString = #file, line: UInt = #line) { XCTAssertEqual(Set(graph.reachableProducts.map { $0.name }), Set(reachableProducts), file: file, line: line) } - public func check( + package func check( reachableBuildTargets: String..., in environment: BuildEnvironment, file: StaticString = #file, @@ -76,7 +76,7 @@ public final class PackageGraphResult { XCTAssertEqual(targets, Set(reachableBuildTargets), file: file, line: line) } - public func check( + package func check( reachableBuildProducts: String..., in environment: BuildEnvironment, file: StaticString = #file, @@ -86,7 +86,7 @@ public final class PackageGraphResult { XCTAssertEqual(products, Set(reachableBuildProducts), file: file, line: line) } - public func checkTarget( + package func checkTarget( _ name: String, file: StaticString = #file, line: UInt = #line, @@ -98,7 +98,7 @@ public final class PackageGraphResult { body(ResolvedTargetResult(target)) } - public func checkProduct( + package func checkProduct( _ name: String, file: StaticString = #file, line: UInt = #line, @@ -110,7 +110,7 @@ public final class PackageGraphResult { body(ResolvedProductResult(target)) } - public func check(testModules: String..., file: StaticString = #file, line: UInt = #line) { + package func check(testModules: String..., file: StaticString = #file, line: UInt = #line) { XCTAssertEqual( graph.allTargets .filter{ $0.type == .test } @@ -118,15 +118,15 @@ public final class PackageGraphResult { .sorted(), testModules.sorted(), file: file, line: line) } - public func find(target: String) -> ResolvedTarget? { + package func find(target: String) -> ResolvedTarget? { return graph.allTargets.first(where: { $0.name == target }) } - public func find(product: String) -> ResolvedProduct? { + package func find(product: String) -> ResolvedProduct? { return graph.allProducts.first(where: { $0.name == product }) } - public func find(package: PackageIdentity) -> ResolvedPackage? { + package func find(package: PackageIdentity) -> ResolvedPackage? { return graph.packages.first(where: { $0.identity == package }) } @@ -148,18 +148,18 @@ public final class PackageGraphResult { } } -public final class ResolvedTargetResult { +package final class ResolvedTargetResult { private let target: ResolvedTarget init(_ target: ResolvedTarget) { self.target = target } - public func check(dependencies: String..., file: StaticString = #file, line: UInt = #line) { + package func check(dependencies: String..., file: StaticString = #file, line: UInt = #line) { XCTAssertEqual(Set(dependencies), Set(target.dependencies.map({ $0.name })), file: file, line: line) } - public func checkDependency( + package func checkDependency( _ name: String, file: StaticString = #file, line: UInt = #line, @@ -171,16 +171,16 @@ public final class ResolvedTargetResult { body(ResolvedTargetDependencyResult(dependency)) } - public func check(type: Target.Kind, file: StaticString = #file, line: UInt = #line) { + package func check(type: Target.Kind, file: StaticString = #file, line: UInt = #line) { XCTAssertEqual(type, target.type, file: file, line: line) } - public func checkDeclaredPlatforms(_ platforms: [String: String], file: StaticString = #file, line: UInt = #line) { + package func checkDeclaredPlatforms(_ platforms: [String: String], file: StaticString = #file, line: UInt = #line) { let targetPlatforms = Dictionary(uniqueKeysWithValues: target.supportedPlatforms.map({ ($0.platform.name, $0.version.versionString) })) XCTAssertEqual(platforms, targetPlatforms, file: file, line: line) } - public func checkDerivedPlatforms(_ platforms: [String: String], file: StaticString = #file, line: UInt = #line) { + package func checkDerivedPlatforms(_ platforms: [String: String], file: StaticString = #file, line: UInt = #line) { let derived = platforms.map { let platform = PlatformRegistry.default.platformByName[$0.key] ?? PackageModel.Platform .custom(name: $0.key, oldestSupportedVersion: $0.value) @@ -193,24 +193,24 @@ public final class ResolvedTargetResult { XCTAssertEqual(platforms, targetPlatforms, file: file, line: line) } - public func checkDerivedPlatformOptions(_ platform: PackageModel.Platform, options: [String], file: StaticString = #file, line: UInt = #line) { + package func checkDerivedPlatformOptions(_ platform: PackageModel.Platform, options: [String], file: StaticString = #file, line: UInt = #line) { let platform = target.getSupportedPlatform(for: platform, usingXCTest: target.type == .test) XCTAssertEqual(platform.options, options, file: file, line: line) } } -public final class ResolvedTargetDependencyResult { +package final class ResolvedTargetDependencyResult { private let dependency: ResolvedTarget.Dependency init(_ dependency: ResolvedTarget.Dependency) { self.dependency = dependency } - public func checkConditions(satisfy environment: BuildEnvironment, file: StaticString = #file, line: UInt = #line) { + package func checkConditions(satisfy environment: BuildEnvironment, file: StaticString = #file, line: UInt = #line) { XCTAssert(dependency.conditions.allSatisfy({ $0.satisfies(environment) }), file: file, line: line) } - public func checkConditions( + package func checkConditions( dontSatisfy environment: BuildEnvironment, file: StaticString = #file, line: UInt = #line @@ -219,27 +219,27 @@ public final class ResolvedTargetDependencyResult { } } -public final class ResolvedProductResult { +package final class ResolvedProductResult { private let product: ResolvedProduct init(_ product: ResolvedProduct) { self.product = product } - public func check(targets: String..., file: StaticString = #file, line: UInt = #line) { + package func check(targets: String..., file: StaticString = #file, line: UInt = #line) { XCTAssertEqual(Set(targets), Set(product.targets.map({ $0.name })), file: file, line: line) } - public func check(type: ProductType, file: StaticString = #file, line: UInt = #line) { + package func check(type: ProductType, file: StaticString = #file, line: UInt = #line) { XCTAssertEqual(type, product.type, file: file, line: line) } - public func checkDeclaredPlatforms(_ platforms: [String: String], file: StaticString = #file, line: UInt = #line) { + package func checkDeclaredPlatforms(_ platforms: [String: String], file: StaticString = #file, line: UInt = #line) { let targetPlatforms = Dictionary(uniqueKeysWithValues: product.supportedPlatforms.map({ ($0.platform.name, $0.version.versionString) })) XCTAssertEqual(platforms, targetPlatforms, file: file, line: line) } - public func checkDerivedPlatforms(_ platforms: [String: String], file: StaticString = #file, line: UInt = #line) { + package func checkDerivedPlatforms(_ platforms: [String: String], file: StaticString = #file, line: UInt = #line) { let derived = platforms.map { let platform = PlatformRegistry.default.platformByName[$0.key] ?? PackageModel.Platform.custom(name: $0.key, oldestSupportedVersion: $0.value) return product.getSupportedPlatform(for: platform, usingXCTest: product.isLinkingXCTest) @@ -248,14 +248,14 @@ public final class ResolvedProductResult { XCTAssertEqual(platforms, targetPlatforms, file: file, line: line) } - public func checkDerivedPlatformOptions(_ platform: PackageModel.Platform, options: [String], file: StaticString = #file, line: UInt = #line) { + package func checkDerivedPlatformOptions(_ platform: PackageModel.Platform, options: [String], file: StaticString = #file, line: UInt = #line) { let platform = product.getSupportedPlatform(for: platform, usingXCTest: product.isLinkingXCTest) XCTAssertEqual(platform.options, options, file: file, line: line) } } extension ResolvedTarget.Dependency { - public var name: String { + package var name: String { switch self { case .target(let target, _): return target.name diff --git a/Sources/SPMTestSupport/ResolvedTarget+Mock.swift b/Sources/SPMTestSupport/ResolvedTarget+Mock.swift index 0a95c7aa8e1..6f109474acf 100644 --- a/Sources/SPMTestSupport/ResolvedTarget+Mock.swift +++ b/Sources/SPMTestSupport/ResolvedTarget+Mock.swift @@ -14,7 +14,7 @@ import PackageGraph import PackageModel extension ResolvedTarget { - public static func mock( + package static func mock( packageIdentity: PackageIdentity, name: String, deps: ResolvedTarget..., diff --git a/Sources/SPMTestSupport/Resolver.swift b/Sources/SPMTestSupport/Resolver.swift index f86371ce196..1e1282b7552 100644 --- a/Sources/SPMTestSupport/Resolver.swift +++ b/Sources/SPMTestSupport/Resolver.swift @@ -13,7 +13,7 @@ import PackageGraph extension PubGrubDependencyResolver { - public func solve(constraints: [Constraint]) -> Result<[DependencyResolverBinding], Error> { + package func solve(constraints: [Constraint]) -> Result<[DependencyResolverBinding], Error> { return solve(constraints: constraints, availableLibraries: [], preferPrebuiltLibraries: false) } } diff --git a/Sources/SPMTestSupport/SwiftPMProduct.swift b/Sources/SPMTestSupport/SwiftPMProduct.swift index 4522060b83e..3338b295be0 100644 --- a/Sources/SPMTestSupport/SwiftPMProduct.swift +++ b/Sources/SPMTestSupport/SwiftPMProduct.swift @@ -19,7 +19,7 @@ import struct TSCBasic.ProcessResult /// Defines the executables used by SwiftPM. /// Contains path to the currently built executable and /// helper method to execute them. -public enum SwiftPM { +package enum SwiftPM { case Build case Package case Registry @@ -44,11 +44,11 @@ extension SwiftPM { } } - public var xctestBinaryPath: AbsolutePath { + package var xctestBinaryPath: AbsolutePath { Self.xctestBinaryPath(for: RelativePath("swift-package-manager")) } - public static func xctestBinaryPath(for executableName: RelativePath) -> AbsolutePath { + package static func xctestBinaryPath(for executableName: RelativePath) -> AbsolutePath { #if canImport(Darwin) for bundle in Bundle.allBundles where bundle.bundlePath.hasSuffix(".xctest") { return try! AbsolutePath(AbsolutePath(validating: bundle.bundlePath).parentDirectory, executableName) @@ -71,7 +71,7 @@ extension SwiftPM { /// /// - Returns: The output of the process. @discardableResult - public func execute( + package func execute( _ args: [String] = [], packagePath: AbsolutePath? = nil, env: [String: String]? = nil @@ -135,7 +135,7 @@ extension SwiftPM { } extension SwiftPM { - public static func packagePath(for packageName: String, packageRoot: AbsolutePath) throws -> AbsolutePath { + package static func packagePath(for packageName: String, packageRoot: AbsolutePath) throws -> AbsolutePath { // FIXME: The directory paths are hard coded right now and should be replaced by --get-package-path // whenever we design that. https://bugs.swift.org/browse/SR-2753 let packagesPath = packageRoot.appending(components: ".build", "checkouts") @@ -148,12 +148,12 @@ extension SwiftPM { } } -public enum SwiftPMError: Error { +package enum SwiftPMError: Error { case packagePathNotFound case executionFailure(underlying: Error, stdout: String, stderr: String) } -public enum SwiftPMProductError: Swift.Error { +package enum SwiftPMProductError: Swift.Error { case packagePathNotFound case executionFailure(error: Swift.Error, output: String, stderr: String) } diff --git a/Sources/SPMTestSupport/Toolchain.swift b/Sources/SPMTestSupport/Toolchain.swift index ff7e403c6b9..35a1a2c3c11 100644 --- a/Sources/SPMTestSupport/Toolchain.swift +++ b/Sources/SPMTestSupport/Toolchain.swift @@ -38,7 +38,7 @@ private func resolveBinDir() throws -> AbsolutePath { } extension SwiftSDK { - public static var `default`: Self { + package static var `default`: Self { get throws { let binDir = try resolveBinDir() return try! SwiftSDK.hostSwiftSDK(binDir) @@ -47,7 +47,7 @@ extension SwiftSDK { } extension UserToolchain { - public static var `default`: Self { + package static var `default`: Self { get throws { return try .init(swiftSDK: SwiftSDK.default) } @@ -56,7 +56,7 @@ extension UserToolchain { extension UserToolchain { /// Helper function to determine if async await actually works in the current environment. - public func supportsSwiftConcurrency() -> Bool { + package func supportsSwiftConcurrency() -> Bool { #if os(macOS) if #available(macOS 12.0, *) { // On macOS 12 and later, concurrency is assumed to work. @@ -67,7 +67,7 @@ extension UserToolchain { do { try testWithTemporaryDirectory { tmpPath in let inputPath = tmpPath.appending("foo.swift") - try localFileSystem.writeFileContents(inputPath, string: "public func foo() async {}\nTask { await foo() }") + try localFileSystem.writeFileContents(inputPath, string: "package func foo() async {}\nTask { await foo() }") let outputPath = tmpPath.appending("foo") let toolchainPath = self.swiftCompilerPath.parentDirectory.parentDirectory let backDeploymentLibPath = toolchainPath.appending(components: "lib", "swift-5.5", "macosx") @@ -88,7 +88,7 @@ extension UserToolchain { } /// Helper function to determine whether serialized diagnostics work properly in the current environment. - public func supportsSerializedDiagnostics(otherSwiftFlags: [String] = []) -> Bool { + package func supportsSerializedDiagnostics(otherSwiftFlags: [String] = []) -> Bool { do { try testWithTemporaryDirectory { tmpPath in let inputPath = tmpPath.appending("best.swift") @@ -112,7 +112,7 @@ extension UserToolchain { } /// Helper function to determine whether we should run SDK-dependent tests. - public func supportsSDKDependentTests() -> Bool { + package func supportsSDKDependentTests() -> Bool { return ProcessInfo.processInfo.environment["SWIFTCI_DISABLE_SDK_DEPENDENT_TESTS"] == nil } } diff --git a/Sources/SPMTestSupport/XCTAssertHelpers.swift b/Sources/SPMTestSupport/XCTAssertHelpers.swift index c771481b072..67adb785fcb 100644 --- a/Sources/SPMTestSupport/XCTAssertHelpers.swift +++ b/Sources/SPMTestSupport/XCTAssertHelpers.swift @@ -26,31 +26,31 @@ import struct TSCUtility.Version @_exported import func TSCTestSupport.XCTAssertResultSuccess @_exported import func TSCTestSupport.XCTAssertThrows -public func XCTAssertFileExists(_ path: AbsolutePath, file: StaticString = #file, line: UInt = #line) { +package func XCTAssertFileExists(_ path: AbsolutePath, file: StaticString = #file, line: UInt = #line) { TSCTestSupport.XCTAssertFileExists(TSCAbsolutePath(path), file: file, line: line) } -public func XCTAssertDirectoryExists(_ path: AbsolutePath, file: StaticString = #file, line: UInt = #line) { +package func XCTAssertDirectoryExists(_ path: AbsolutePath, file: StaticString = #file, line: UInt = #line) { TSCTestSupport.XCTAssertDirectoryExists(TSCAbsolutePath(path), file: file, line: line) } -public func XCTAssertNoSuchPath(_ path: AbsolutePath, file: StaticString = #file, line: UInt = #line) { +package func XCTAssertNoSuchPath(_ path: AbsolutePath, file: StaticString = #file, line: UInt = #line) { TSCTestSupport.XCTAssertNoSuchPath(TSCAbsolutePath(path), file: file, line: line) } -public func XCTAssertEqual (_ lhs:(T,U), _ rhs:(T,U), file: StaticString = #file, line: UInt = #line) { +package func XCTAssertEqual (_ lhs:(T,U), _ rhs:(T,U), file: StaticString = #file, line: UInt = #line) { TSCTestSupport.XCTAssertEqual(lhs, rhs, file: file, line: line) } -public func XCTSkipIfCI(file: StaticString = #filePath, line: UInt = #line) throws { +package func XCTSkipIfCI(file: StaticString = #filePath, line: UInt = #line) throws { if let ci = ProcessInfo.processInfo.environment["CI"] as? NSString, ci.boolValue { throw XCTSkip("Skipping because the test is being run on CI", file: file, line: line) } } /// An `async`-friendly replacement for `XCTAssertThrowsError`. -public func XCTAssertAsyncThrowsError( +package func XCTAssertAsyncThrowsError( _ expression: @autoclosure () async throws -> T, _ message: @autoclosure () -> String = "", file: StaticString = #filePath, @@ -66,7 +66,7 @@ public func XCTAssertAsyncThrowsError( } -public func XCTAssertBuilds( +package func XCTAssertBuilds( _ path: AbsolutePath, configurations: Set = [.Debug, .Release], extraArgs: [String] = [], @@ -94,7 +94,7 @@ public func XCTAssertBuilds( } } -public func XCTAssertSwiftTest( +package func XCTAssertSwiftTest( _ path: AbsolutePath, configuration: Configuration = .Debug, extraArgs: [String] = [], @@ -121,7 +121,7 @@ public func XCTAssertSwiftTest( } @discardableResult -public func XCTAssertBuildFails( +package func XCTAssertBuildFails( _ path: AbsolutePath, Xcc: [String] = [], Xld: [String] = [], @@ -137,7 +137,7 @@ public func XCTAssertBuildFails( return failure } -public func XCTAssertEqual( +package func XCTAssertEqual( _ assignment: [(container: T, version: Version)], _ expected: [T: Version], file: StaticString = #file, @@ -150,7 +150,7 @@ public func XCTAssertEqual( XCTAssertEqual(actual, expected, file: file, line: line) } -public func XCTAssertAsyncTrue( +package func XCTAssertAsyncTrue( _ expression: @autoclosure () async throws -> Bool, _ message: @autoclosure () -> String = "", file: StaticString = #filePath, @@ -160,7 +160,7 @@ public func XCTAssertAsyncTrue( XCTAssertTrue(result, message(), file: file, line: line) } -public func XCTAssertAsyncFalse( +package func XCTAssertAsyncFalse( _ expression: @autoclosure () async throws -> Bool, _ message: @autoclosure () -> String = "", file: StaticString = #filePath, @@ -170,7 +170,7 @@ public func XCTAssertAsyncFalse( XCTAssertFalse(result, message(), file: file, line: line) } -public func XCTAssertThrowsCommandExecutionError( +package func XCTAssertThrowsCommandExecutionError( _ expression: @autoclosure () throws -> T, _ message: @autoclosure () -> String = "", file: StaticString = #filePath, @@ -187,7 +187,7 @@ public func XCTAssertThrowsCommandExecutionError( } } -public func XCTAssertAsyncEqual( +package func XCTAssertAsyncEqual( _ expression1: @autoclosure () async throws -> T, _ expression2: @autoclosure () async throws -> T, _ message: @autoclosure () -> String = "", @@ -202,7 +202,7 @@ public func XCTAssertAsyncEqual( struct XCAsyncTestErrorWhileUnwrappingOptional: Error {} -public func XCTAsyncUnwrap( +package func XCTAsyncUnwrap( _ expression: @autoclosure () async throws -> T?, _ message: @autoclosure () -> String = "", file: StaticString = #filePath, @@ -216,8 +216,8 @@ public func XCTAsyncUnwrap( } -public struct CommandExecutionError: Error { - public let result: ProcessResult - public let stdout: String - public let stderr: String +package struct CommandExecutionError: Error { + package let result: ProcessResult + package let stdout: String + package let stderr: String } diff --git a/Sources/SPMTestSupport/misc.swift b/Sources/SPMTestSupport/misc.swift index f4d8e10dea1..8a36a09237d 100644 --- a/Sources/SPMTestSupport/misc.swift +++ b/Sources/SPMTestSupport/misc.swift @@ -34,7 +34,7 @@ import enum TSCUtility.Git @_exported import enum TSCTestSupport.StringPattern /// Test helper utility for executing a block with a temporary directory. -public func testWithTemporaryDirectory( +package func testWithTemporaryDirectory( function: StaticString = #function, body: (AbsolutePath) throws -> Void ) throws { @@ -49,7 +49,7 @@ public func testWithTemporaryDirectory( } @discardableResult -public func testWithTemporaryDirectory( +package func testWithTemporaryDirectory( function: StaticString = #function, body: (AbsolutePath) async throws -> Result ) async throws -> Result { @@ -74,7 +74,7 @@ public func testWithTemporaryDirectory( /// The temporary copy is deleted after the block returns. The fixture name may /// contain `/` characters, which are treated as path separators, exactly as if /// the name were a relative path. -@discardableResult public func fixture( +@discardableResult package func fixture( name: String, createGitRepo: Bool = true, file: StaticString = #file, @@ -112,7 +112,7 @@ public func testWithTemporaryDirectory( } } -@discardableResult public func fixture( +@discardableResult package func fixture( name: String, createGitRepo: Bool = true, file: StaticString = #file, @@ -197,7 +197,7 @@ fileprivate func setup(fixtureDir: AbsolutePath, in tmpDirPath: AbsolutePath, co /// Test-helper function that creates a new Git repository in a directory. The new repository will contain /// exactly one empty file unless `addFile` is `false`, and if a tag name is provided, a tag with that name will be /// created. -public func initGitRepo( +package func initGitRepo( _ dir: AbsolutePath, tag: String? = nil, addFile: Bool = true, @@ -207,7 +207,7 @@ public func initGitRepo( initGitRepo(dir, tags: tag.flatMap { [$0] } ?? [], addFile: addFile, file: file, line: line) } -public func initGitRepo( +package func initGitRepo( _ dir: AbsolutePath, tags: [String], addFile: Bool = true, @@ -237,7 +237,7 @@ public func initGitRepo( } @discardableResult -public func executeSwiftBuild( +package func executeSwiftBuild( _ packagePath: AbsolutePath, configuration: Configuration = .Debug, extraArgs: [String] = [], @@ -251,7 +251,7 @@ public func executeSwiftBuild( } @discardableResult -public func executeSwiftRun( +package func executeSwiftRun( _ packagePath: AbsolutePath, _ executable: String, configuration: Configuration = .Debug, @@ -267,7 +267,7 @@ public func executeSwiftRun( } @discardableResult -public func executeSwiftPackage( +package func executeSwiftPackage( _ packagePath: AbsolutePath, configuration: Configuration = .Debug, extraArgs: [String] = [], @@ -281,7 +281,7 @@ public func executeSwiftPackage( } @discardableResult -public func executeSwiftTest( +package func executeSwiftTest( _ packagePath: AbsolutePath, configuration: Configuration = .Debug, extraArgs: [String] = [], @@ -321,7 +321,7 @@ private func swiftArgs( renamed: "loadModulesGraph", message: "Renamed for consistency: the type of this functions return value is named `ModulesGraph`." ) -public func loadPackageGraph( +package func loadPackageGraph( identityResolver: IdentityResolver = DefaultIdentityResolver(), fileSystem: FileSystem, manifests: [Manifest], @@ -347,7 +347,7 @@ public func loadPackageGraph( ) } -public func loadModulesGraph( +package func loadModulesGraph( identityResolver: IdentityResolver = DefaultIdentityResolver(), fileSystem: FileSystem, manifests: [Manifest], @@ -393,22 +393,22 @@ public func loadModulesGraph( ) } -public let emptyZipFile = ByteString([0x80, 0x75, 0x05, 0x06] + [UInt8](repeating: 0x00, count: 18)) +package let emptyZipFile = ByteString([0x80, 0x75, 0x05, 0x06] + [UInt8](repeating: 0x00, count: 18)) extension FileSystem { @_disfavoredOverload - public func createEmptyFiles(at root: AbsolutePath, files: String...) { + package func createEmptyFiles(at root: AbsolutePath, files: String...) { self.createEmptyFiles(at: TSCAbsolutePath(root), files: files) } @_disfavoredOverload - public func createEmptyFiles(at root: AbsolutePath, files: [String]) { + package func createEmptyFiles(at root: AbsolutePath, files: [String]) { self.createEmptyFiles(at: TSCAbsolutePath(root), files: files) } } extension URL { - public init(_ value: StringLiteralType) { + package init(_ value: StringLiteralType) { self.init(string: value)! } } @@ -426,13 +426,13 @@ extension PackageIdentity { } extension PackageIdentity { - public static func registry(_ value: String) -> RegistryIdentity { + package static func registry(_ value: String) -> RegistryIdentity { Self.plain(value).registry! } } extension AbsolutePath { - public init(_ value: StringLiteralType) { + package init(_ value: StringLiteralType) { try! self.init(validating: value) } } @@ -444,14 +444,14 @@ extension AbsolutePath { } extension AbsolutePath { - public init(_ path: StringLiteralType, relativeTo basePath: AbsolutePath) { + package init(_ path: StringLiteralType, relativeTo basePath: AbsolutePath) { try! self.init(validating: path, relativeTo: basePath) } } extension RelativePath { @available(*, deprecated, message: "use direct string instead") - public init(static path: StaticString) { + package init(static path: StaticString) { let pathString = path.withUTF8Buffer { String(decoding: $0, as: UTF8.self) } @@ -460,7 +460,7 @@ extension RelativePath { } extension RelativePath { - public init(_ value: StringLiteralType) { + package init(_ value: StringLiteralType) { try! self.init(validating: value) } } @@ -472,7 +472,7 @@ extension RelativePath { } extension InitPackage { - public convenience init( + package convenience init( name: String, packageType: PackageType, supportedTestingLibraries: Set = [.xctest], diff --git a/Tests/PackageGraphPerformanceTests/DependencyResolverPerfTests.swift b/Tests/PackageGraphPerformanceTests/DependencyResolverPerfTests.swift index 124ddc706be..9d92488bbf3 100644 --- a/Tests/PackageGraphPerformanceTests/DependencyResolverPerfTests.swift +++ b/Tests/PackageGraphPerformanceTests/DependencyResolverPerfTests.swift @@ -95,7 +95,7 @@ class DependencyResolverRealWorldPerfTests: XCTestCasePerf { // MARK: - JSON -public extension MockDependencyGraph { +extension MockDependencyGraph { init(_ json: JSON) { guard case .dictionary(let dict) = json else { fatalError() } guard case .string(let name)? = dict["name"] else { fatalError() } From f042ffdd9cefdf62859757f4bc586567147a3a14 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Wed, 28 Feb 2024 02:26:31 +0000 Subject: [PATCH 034/159] Enable `AccessLevelOnImport` in `SourceKitLSPAPI` (#7366) This prevents unnecessary API details from leaking outside of the SwiftPM package via `SourceKitLSPAPI` product. --- Package.swift | 3 ++- .../SourceKitLSPAPI/BuildDescription.swift | 24 +++++++++++-------- .../PluginTargetBuildDescription.swift | 9 ++++--- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/Package.swift b/Package.swift index f0e7d652a79..80269ac3309 100644 --- a/Package.swift +++ b/Package.swift @@ -168,7 +168,8 @@ let package = Package( "Build", "SPMBuildCore" ], - exclude: ["CMakeLists.txt"] + exclude: ["CMakeLists.txt"], + swiftSettings: [.enableExperimentalFeature("AccessLevelOnImport")] ), // MARK: SwiftPM specific support libraries diff --git a/Sources/SourceKitLSPAPI/BuildDescription.swift b/Sources/SourceKitLSPAPI/BuildDescription.swift index 824df666a0d..1f560d63464 100644 --- a/Sources/SourceKitLSPAPI/BuildDescription.swift +++ b/Sources/SourceKitLSPAPI/BuildDescription.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2023 Apple Inc. and the Swift project authors +// Copyright (c) 2023-2024 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information @@ -10,15 +10,18 @@ // //===----------------------------------------------------------------------===// -import Foundation +import struct Foundation.URL -/*private*/ import struct Basics.AbsolutePath -/*private*/ import func Basics.resolveSymlinks -// FIXME: should not import this module -import Build -// FIXME: should be internal imports -import PackageGraph -/*private*/ import SPMBuildCore +private import struct Basics.AbsolutePath +private import func Basics.resolveSymlinks + +private import SPMBuildCore + +// FIXME: should import these module with `private` or `internal` access control +import class Build.BuildPlan +import class Build.ClangTargetBuildDescription +import class Build.SwiftTargetBuildDescription +import struct PackageGraph.ResolvedTarget public protocol BuildTarget { var sources: [URL] { get } @@ -49,7 +52,8 @@ private struct WrappedSwiftTargetBuildDescription: BuildTarget { } func compileArguments(for fileURL: URL) throws -> [String] { - // Note: we ignore the `fileURL` here as the expectation is that we get a commandline for the entire target in case of Swift. + // Note: we ignore the `fileURL` here as the expectation is that we get a command line for the entire target + // in case of Swift. return try description.emitCommandLine(scanInvocation: false) } } diff --git a/Sources/SourceKitLSPAPI/PluginTargetBuildDescription.swift b/Sources/SourceKitLSPAPI/PluginTargetBuildDescription.swift index f1aa33b86d1..8524908f5c1 100644 --- a/Sources/SourceKitLSPAPI/PluginTargetBuildDescription.swift +++ b/Sources/SourceKitLSPAPI/PluginTargetBuildDescription.swift @@ -10,14 +10,13 @@ // //===----------------------------------------------------------------------===// -import Foundation +import struct Foundation.URL -// FIXME: should be an internal import import struct PackageGraph.ResolvedTarget -/*private*/ import class PackageLoading.ManifestLoader -/*private*/ import struct PackageModel.ToolsVersion -/*private*/ import class PackageModel.UserToolchain +private import class PackageLoading.ManifestLoader +internal import struct PackageModel.ToolsVersion +private import class PackageModel.UserToolchain struct PluginTargetBuildDescription: BuildTarget { private let target: ResolvedTarget From f33508303bc76e67ace55e8c6aa64c768f04f978 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Wed, 28 Feb 2024 22:42:11 -0800 Subject: [PATCH 035/159] =?UTF-8?q?Don=E2=80=99t=20include=20compiler=20in?= =?UTF-8?q?=20the=20command=20line=20arguments=20(#7379)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The compiler itself is part of the command line invocation, not an argument in itself. rdar://123765692 --- Sources/SourceKitLSPAPI/BuildDescription.swift | 8 ++++++-- Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Sources/SourceKitLSPAPI/BuildDescription.swift b/Sources/SourceKitLSPAPI/BuildDescription.swift index 1f560d63464..d9287580bae 100644 --- a/Sources/SourceKitLSPAPI/BuildDescription.swift +++ b/Sources/SourceKitLSPAPI/BuildDescription.swift @@ -36,7 +36,9 @@ extension ClangTargetBuildDescription: BuildTarget { public func compileArguments(for fileURL: URL) throws -> [String] { let filePath = try resolveSymlinks(try AbsolutePath(validating: fileURL.path)) - return try self.emitCommandLine(for: filePath) + let commandLine = try self.emitCommandLine(for: filePath) + // First element on the command line is the compiler itself, not an argument. + return Array(commandLine.dropFirst()) } } @@ -54,7 +56,9 @@ private struct WrappedSwiftTargetBuildDescription: BuildTarget { func compileArguments(for fileURL: URL) throws -> [String] { // Note: we ignore the `fileURL` here as the expectation is that we get a command line for the entire target // in case of Swift. - return try description.emitCommandLine(scanInvocation: false) + let commandLine = try description.emitCommandLine(scanInvocation: false) + // First element on the command line is the compiler itself, not an argument. + return Array(commandLine.dropFirst()) } } diff --git a/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift b/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift index d42a55db9c4..127710d0785 100644 --- a/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift +++ b/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift @@ -51,8 +51,8 @@ class SourceKitLSPAPITests: XCTestCase { ) let description = BuildDescription(buildPlan: plan) - try description.checkArguments(for: "exe", graph: graph, partialArguments: ["/fake/path/to/swiftc", "-module-name", "exe", "-emit-dependencies", "-emit-module", "-emit-module-path", "/path/to/build/debug/exe.build/exe.swiftmodule"]) - try description.checkArguments(for: "lib", graph: graph, partialArguments: ["/fake/path/to/swiftc", "-module-name", "lib", "-emit-dependencies", "-emit-module", "-emit-module-path", "/path/to/build/debug/Modules/lib.swiftmodule"]) + try description.checkArguments(for: "exe", graph: graph, partialArguments: ["-module-name", "exe", "-emit-dependencies", "-emit-module", "-emit-module-path", "/path/to/build/debug/exe.build/exe.swiftmodule"]) + try description.checkArguments(for: "lib", graph: graph, partialArguments: ["-module-name", "lib", "-emit-dependencies", "-emit-module", "-emit-module-path", "/path/to/build/debug/Modules/lib.swiftmodule"]) } } From 64adc92bd6db10f885aec2dd0655d7407de29ff6 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Thu, 29 Feb 2024 03:53:30 -0500 Subject: [PATCH 036/159] Add swift-testing support to `swift build --build-tests` (#7377) This PR generalizes the enable/disable XCTest/swift-testing options from `swift test` and `swift package init` and adds them to `swift build --build-tests` so that it is possible to build all test content without actually running the tests. If neither flag is specified, the default test content is built (always XCTest, and swift-testing if it's a dependency, same as `swift test`.) Resolves #7375. --- Sources/Commands/PackageCommands/Init.swift | 20 ++--- Sources/Commands/SwiftBuildCommand.swift | 32 +++++++ Sources/Commands/SwiftTestCommand.swift | 77 +++------------- Sources/CoreCommands/Options.swift | 98 +++++++++++++++++++++ 4 files changed, 151 insertions(+), 76 deletions(-) diff --git a/Sources/Commands/PackageCommands/Init.swift b/Sources/Commands/PackageCommands/Init.swift index 847af06b803..05090069eea 100644 --- a/Sources/Commands/PackageCommands/Init.swift +++ b/Sources/Commands/PackageCommands/Init.swift @@ -42,17 +42,9 @@ extension SwiftPackageCommand { """)) var initMode: InitPackage.PackageType = .library - /// Whether to enable support for XCTest. - @Flag(name: .customLong("xctest"), - inversion: .prefixedEnableDisable, - help: "Enable support for XCTest") - var enableXCTestSupport: Bool = true - - /// Whether to enable support for swift-testing. - @Flag(name: .customLong("experimental-swift-testing"), - inversion: .prefixedEnableDisable, - help: "Enable experimental support for swift-testing") - var enableSwiftTestingLibrarySupport: Bool = false + /// Which testing libraries to use (and any related options.) + @OptionGroup() + var testLibraryOptions: TestLibraryOptions @Option(name: .customLong("name"), help: "Provide custom package name") var packageName: String? @@ -62,11 +54,13 @@ extension SwiftPackageCommand { throw InternalError("Could not find the current working directory") } + // NOTE: Do not use testLibraryOptions.enabledTestingLibraries(swiftCommandState:) here + // because the package doesn't exist yet, so there are no dependencies for it to query. var testingLibraries: Set = [] - if enableXCTestSupport { + if testLibraryOptions.enableXCTestSupport { testingLibraries.insert(.xctest) } - if enableSwiftTestingLibrarySupport { + if testLibraryOptions.explicitlyEnableSwiftTestingLibrarySupport == true { testingLibraries.insert(.swiftTesting) } let packageName = self.packageName ?? cwd.basename diff --git a/Sources/Commands/SwiftBuildCommand.swift b/Sources/Commands/SwiftBuildCommand.swift index 468f3b8d25d..e67b006865f 100644 --- a/Sources/Commands/SwiftBuildCommand.swift +++ b/Sources/Commands/SwiftBuildCommand.swift @@ -96,6 +96,21 @@ struct BuildCommandOptions: ParsableArguments { /// If should link the Swift stdlib statically. @Flag(name: .customLong("static-swift-stdlib"), inversion: .prefixedNo, help: "Link Swift stdlib statically") public var shouldLinkStaticSwiftStdlib: Bool = false + + /// Which testing libraries to use (and any related options.) + @OptionGroup() + var testLibraryOptions: TestLibraryOptions + + func validate() throws { + // If --build-tests was not specified, it does not make sense to enable + // or disable either testing library. + if !buildTests { + if testLibraryOptions.explicitlyEnableXCTestSupport != nil + || testLibraryOptions.explicitlyEnableSwiftTestingLibrarySupport != nil { + throw StringError("pass --build-tests to build test targets") + } + } + } } /// swift-build command namespace @@ -137,9 +152,26 @@ public struct SwiftBuildCommand: AsyncSwiftCommand { guard let subset = options.buildSubset(observabilityScope: swiftCommandState.observabilityScope) else { throw ExitCode.failure } + if case .allIncludingTests = subset { + var buildParameters = try swiftCommandState.productsBuildParameters + for library in try options.testLibraryOptions.enabledTestingLibraries(swiftCommandState: swiftCommandState) { + buildParameters.testingParameters = .init( + configuration: buildParameters.configuration, + targetTriple: buildParameters.triple, + library: library + ) + try build(swiftCommandState, subset: subset, buildParameters: buildParameters) + } + } else { + try build(swiftCommandState, subset: subset) + } + } + + private func build(_ swiftCommandState: SwiftCommandState, subset: BuildSubset, buildParameters: BuildParameters? = nil) throws { let buildSystem = try swiftCommandState.createBuildSystem( explicitProduct: options.product, shouldLinkStaticSwiftStdlib: options.shouldLinkStaticSwiftStdlib, + productsBuildParameters: buildParameters, // command result output goes on stdout // ie "swift build" should output to stdout outputStream: TSCBasic.stdoutStream diff --git a/Sources/Commands/SwiftTestCommand.swift b/Sources/Commands/SwiftTestCommand.swift index 76c76258e70..6d62db48a93 100644 --- a/Sources/Commands/SwiftTestCommand.swift +++ b/Sources/Commands/SwiftTestCommand.swift @@ -75,63 +75,6 @@ struct SharedOptions: ParsableArguments { /// to choose from (usually in multiroot packages). @Option(help: .hidden) var testProduct: String? - - /// Whether to enable support for XCTest. - @Flag(name: .customLong("xctest"), - inversion: .prefixedEnableDisable, - help: "Enable support for XCTest") - var enableXCTestSupport: Bool = true - - /// Storage for whether to enable support for swift-testing. - @Flag(name: .customLong("experimental-swift-testing"), - inversion: .prefixedEnableDisable, - help: "Enable experimental support for swift-testing") - var _enableSwiftTestingLibrarySupport: Bool? - - /// Whether to enable support for swift-testing. - func enableSwiftTestingLibrarySupport(swiftCommandState: SwiftCommandState) throws -> Bool { - // Honor the user's explicit command-line selection, if any. - if let callerSuppliedValue = _enableSwiftTestingLibrarySupport { - return callerSuppliedValue - } - - // If the active package has a dependency on swift-testing, automatically enable support for it so that extra steps are not needed. - let workspace = try swiftCommandState.getActiveWorkspace() - let root = try swiftCommandState.getWorkspaceRoot() - let rootManifests = try temp_await { - workspace.loadRootManifests( - packages: root.packages, - observabilityScope: swiftCommandState.observabilityScope, - completion: $0 - ) - } - - // Is swift-testing among the dependencies of the package being built? - // If so, enable support. - let isEnabledByDependency = rootManifests.values.lazy - .flatMap(\.dependencies) - .map(\.identity) - .map(String.init(describing:)) - .contains("swift-testing") - if isEnabledByDependency { - swiftCommandState.observabilityScope.emit(debug: "Enabling swift-testing support due to its presence as a package dependency.") - return true - } - - // Is swift-testing the package being built itself (unlikely)? If so, - // enable support. - let isEnabledByName = root.packages.lazy - .map(PackageIdentity.init(path:)) - .map(String.init(describing:)) - .contains("swift-testing") - if isEnabledByName { - swiftCommandState.observabilityScope.emit(debug: "Enabling swift-testing support because it is a root package.") - return true - } - - // Default to disabled since swift-testing is experimental (opt-in.) - return false - } } struct TestCommandOptions: ParsableArguments { @@ -141,6 +84,10 @@ struct TestCommandOptions: ParsableArguments { @OptionGroup() var sharedOptions: SharedOptions + /// Which testing libraries to use (and any related options.) + @OptionGroup() + var testLibraryOptions: TestLibraryOptions + /// If tests should run in parallel mode. @Flag(name: .customLong("parallel"), inversion: .prefixedNo, @@ -425,10 +372,10 @@ public struct SwiftTestCommand: SwiftCommand { let command = try List.parse() try command.run(swiftCommandState) } else { - if try options.sharedOptions.enableSwiftTestingLibrarySupport(swiftCommandState: swiftCommandState) { + if try options.testLibraryOptions.enableSwiftTestingLibrarySupport(swiftCommandState: swiftCommandState) { try swiftTestingRun(swiftCommandState) } - if options.sharedOptions.enableXCTestSupport { + if options.testLibraryOptions.enableXCTestSupport { try xctestRun(swiftCommandState) } } @@ -624,7 +571,7 @@ public struct SwiftTestCommand: SwiftCommand { throw StringError("'--num-workers' must be greater than zero") } - if !options.sharedOptions.enableXCTestSupport { + if !options.testLibraryOptions.enableXCTestSupport { throw StringError("'--num-workers' is only supported when testing with XCTest") } } @@ -680,6 +627,10 @@ extension SwiftTestCommand { @OptionGroup() var sharedOptions: SharedOptions + /// Which testing libraries to use (and any related options.) + @OptionGroup() + var testLibraryOptions: TestLibraryOptions + // for deprecated passthrough from SwiftTestTool (parse will fail otherwise) @Flag(name: [.customLong("list-tests"), .customShort("l")], help: .hidden) var _deprecated_passthrough: Bool = false @@ -752,10 +703,10 @@ extension SwiftTestCommand { // MARK: - Common implementation func run(_ swiftCommandState: SwiftCommandState) throws { - if try sharedOptions.enableSwiftTestingLibrarySupport(swiftCommandState: swiftCommandState) { + if try testLibraryOptions.enableSwiftTestingLibrarySupport(swiftCommandState: swiftCommandState) { try swiftTestingRun(swiftCommandState) } - if sharedOptions.enableXCTestSupport { + if testLibraryOptions.enableXCTestSupport { try xctestRun(swiftCommandState) } } @@ -1327,7 +1278,7 @@ extension SwiftCommandState { experimentalTestOutput: options.enableExperimentalTestOutput, library: library ) - if try options.sharedOptions.enableSwiftTestingLibrarySupport(swiftCommandState: self) { + if try options.testLibraryOptions.enableSwiftTestingLibrarySupport(swiftCommandState: self) { result.flags.swiftCompilerFlags += ["-DSWIFT_PM_SUPPORTS_SWIFT_TESTING"] } return result diff --git a/Sources/CoreCommands/Options.swift b/Sources/CoreCommands/Options.swift index b44bcb1e85b..ec54d23d6c4 100644 --- a/Sources/CoreCommands/Options.swift +++ b/Sources/CoreCommands/Options.swift @@ -15,6 +15,7 @@ import ArgumentParser import var Basics.localFileSystem import struct Basics.AbsolutePath import struct Basics.Triple +import func Basics.temp_await import struct Foundation.URL @@ -22,8 +23,10 @@ import enum PackageModel.BuildConfiguration import struct PackageModel.BuildFlags import struct PackageModel.EnabledSanitizers import struct PackageModel.PackageIdentity +import class PackageModel.Manifest import enum PackageModel.Sanitizer +import struct SPMBuildCore.BuildParameters @_spi(SwiftPMInternal) import struct SPMBuildCore.BuildSystemProvider @@ -31,6 +34,7 @@ import struct TSCBasic.StringError import struct TSCUtility.Version +import class Workspace.Workspace import struct Workspace.WorkspaceConfiguration @_spi(SwiftPMInternal) @@ -540,6 +544,100 @@ public struct LinkerOptions: ParsableArguments { public var shouldDisableLocalRpath: Bool = false } +/// Which testing libraries to use (and any related options.) +@_spi(SwiftPMInternal) +public struct TestLibraryOptions: ParsableArguments { + public init() {} + + /// Whether to enable support for XCTest (as explicitly specified by the user.) + /// + /// Callers will generally want to use ``enableXCTestSupport`` since it will + /// have the correct default value if the user didn't specify one. + @Flag(name: .customLong("xctest"), + inversion: .prefixedEnableDisable, + help: "Enable support for XCTest") + public var explicitlyEnableXCTestSupport: Bool? + + /// Whether to enable support for XCTest. + public var enableXCTestSupport: Bool { + // Default to enabled. + explicitlyEnableXCTestSupport ?? true + } + + /// Whether to enable support for swift-testing (as explicitly specified by the user.) + /// + /// Callers (other than `swift package init`) will generally want to use + /// ``enableSwiftTestingLibrarySupport(swiftCommandState:)`` since it will + /// take into account whether the package has a dependency on swift-testing. + @Flag(name: .customLong("experimental-swift-testing"), + inversion: .prefixedEnableDisable, + help: "Enable experimental support for swift-testing") + public var explicitlyEnableSwiftTestingLibrarySupport: Bool? + + /// Whether to enable support for swift-testing. + public func enableSwiftTestingLibrarySupport( + swiftCommandState: SwiftCommandState + ) throws -> Bool { + // Honor the user's explicit command-line selection, if any. + if let callerSuppliedValue = explicitlyEnableSwiftTestingLibrarySupport { + return callerSuppliedValue + } + + // If the active package has a dependency on swift-testing, automatically enable support for it so that extra steps are not needed. + let workspace = try swiftCommandState.getActiveWorkspace() + let root = try swiftCommandState.getWorkspaceRoot() + let rootManifests = try temp_await { + workspace.loadRootManifests( + packages: root.packages, + observabilityScope: swiftCommandState.observabilityScope, + completion: $0 + ) + } + + // Is swift-testing among the dependencies of the package being built? + // If so, enable support. + let isEnabledByDependency = rootManifests.values.lazy + .flatMap(\.dependencies) + .map(\.identity) + .map(String.init(describing:)) + .contains("swift-testing") + if isEnabledByDependency { + swiftCommandState.observabilityScope.emit(debug: "Enabling swift-testing support due to its presence as a package dependency.") + return true + } + + // Is swift-testing the package being built itself (unlikely)? If so, + // enable support. + let isEnabledByName = root.packages.lazy + .map(PackageIdentity.init(path:)) + .map(String.init(describing:)) + .contains("swift-testing") + if isEnabledByName { + swiftCommandState.observabilityScope.emit(debug: "Enabling swift-testing support because it is a root package.") + return true + } + + // Default to disabled since swift-testing is experimental (opt-in.) + return false + } + + /// Get the set of enabled testing libraries. + public func enabledTestingLibraries( + swiftCommandState: SwiftCommandState + ) throws -> Set { + var result = Set() + + if enableXCTestSupport { + result.insert(.xctest) + } + if try enableSwiftTestingLibrarySupport(swiftCommandState: swiftCommandState) { + result.insert(.swiftTesting) + } + + return result + } +} + // MARK: - Extensions extension BuildConfiguration { From 9337c5ec7e3903cc1792cda1d484462c6d6ab8a5 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 29 Feb 2024 17:15:07 +0000 Subject: [PATCH 037/159] Hide CLI commands API with `package` access control (#7381) Known clients of SwiftPM have not adopted these CLI APIs that were previously marked as `public`. To prevent accidental adoption by clients in the future, we should use `package` access control instead. Also replaced remaining usages of `@_spi(SwiftPMInternal)` with `package` across all of our codebase. --- .../NinjaProgressAnimation.swift | 3 +- .../PercentProgressAnimation.swift | 3 +- .../ProgressAnimationProtocol.swift | 6 +- .../ThrottledProgressAnimation.swift | 10 +- .../ProductBuildDescription.swift | 1 - .../SwiftTargetBuildDescription.swift | 1 - Sources/Build/BuildOperation.swift | 6 +- ...dOperationBuildSystemDelegateHandler.swift | 2 - .../Build/BuildPlan/BuildPlan+Product.swift | 1 - Sources/Build/BuildPlan/BuildPlan.swift | 3 +- .../Commands/CommandWorkspaceDelegate.swift | 11 +- .../Commands/PackageCommands/APIDiff.swift | 2 - .../PackageCommands/ArchiveSource.swift | 3 +- .../PackageCommands/CompletionCommand.swift | 1 - .../PackageCommands/ComputeChecksum.swift | 1 - Sources/Commands/PackageCommands/Config.swift | 1 - .../Commands/PackageCommands/Describe.swift | 1 - .../PackageCommands/DumpCommands.swift | 2 - .../PackageCommands/EditCommands.swift | 1 - Sources/Commands/PackageCommands/Format.swift | 1 - Sources/Commands/PackageCommands/Init.swift | 3 +- .../PackageCommands/InstalledPackages.swift | 2 - Sources/Commands/PackageCommands/Learn.swift | 1 - .../PackageCommands/PluginCommand.swift | 2 - .../PackageCommands/ResetCommands.swift | 1 - .../Commands/PackageCommands/Resolve.swift | 1 - .../PackageCommands/ShowDependencies.swift | 5 +- .../PackageCommands/SwiftPackageCommand.swift | 9 +- .../PackageCommands/ToolsVersionCommand.swift | 1 - Sources/Commands/PackageCommands/Update.swift | 1 - Sources/Commands/Snippets/CardStack.swift | 1 - .../Commands/Snippets/Cards/SnippetCard.swift | 2 - .../Snippets/Cards/SnippetGroupCard.swift | 1 - Sources/Commands/Snippets/Cards/TopCard.swift | 1 - Sources/Commands/Snippets/Colorful.swift | 46 ++-- Sources/Commands/SwiftBuildCommand.swift | 18 +- Sources/Commands/SwiftRunCommand.swift | 15 +- Sources/Commands/SwiftTestCommand.swift | 22 +- Sources/Commands/Utilities/APIDigester.swift | 14 +- .../Commands/Utilities/MultiRootSupport.swift | 6 +- .../Commands/Utilities/PluginDelegate.swift | 10 +- .../Utilities/SymbolGraphExtract.swift | 8 +- .../Commands/Utilities/TestingSupport.swift | 1 - Sources/CoreCommands/BuildSystemSupport.swift | 6 +- Sources/CoreCommands/Options.swift | 196 +++++++++--------- .../SwiftCommandObservabilityHandler.swift | 9 +- Sources/CoreCommands/SwiftCommandState.swift | 100 ++++----- .../DriverSupport/DriverSupportUtils.swift | 3 +- .../PackageCollectionsCommand.swift | 8 +- Sources/PackageModel/SwiftSDKs/SwiftSDK.swift | 3 +- .../SwiftSDKs/SwiftSDKBundleStore.swift | 1 - Sources/PackageModel/Target/Target.swift | 9 +- .../PackageRegistryCommand+Auth.swift | 2 - .../PackageRegistryCommand+Publish.swift | 2 - .../PackageRegistryCommand.swift | 8 +- .../BuildParameters+Driver.swift | 3 +- .../BuildSystem/BuildSystem.swift | 12 +- .../BuildSystem/BuildSystemCommand.swift | 3 +- .../BuildSystem/BuildSystemDelegate.swift | 5 +- .../ResolvedPackage+Extensions.swift | 3 +- Sources/SPMBuildCore/Triple+Extensions.swift | 3 +- .../SPMTestSupport/MockBuildTestHelper.swift | 1 - .../Configuration/ConfigureSwiftSDK.swift | 6 +- Sources/SwiftSDKCommand/InstallSwiftSDK.swift | 7 +- Sources/SwiftSDKCommand/ListSwiftSDKs.swift | 6 +- Sources/SwiftSDKCommand/RemoveSwiftSDK.swift | 6 +- Sources/SwiftSDKCommand/SwiftSDKCommand.swift | 6 +- .../SwiftSDKCommand/SwiftSDKSubcommand.swift | 2 +- Sources/XCBuildSupport/PIFBuilder.swift | 1 - Sources/XCBuildSupport/XCBuildDelegate.swift | 4 +- Sources/XCBuildSupport/XcodeBuildSystem.swift | 5 +- Sources/swift-bootstrap/main.swift | 30 ++- Sources/swift-build/Entrypoint.swift | 1 - Sources/swift-package-manager/SwiftPM.swift | 1 - Sources/swift-run/Entrypoint.swift | 1 - Sources/swift-test/main.swift | 1 - .../BasicsTests/ProgressAnimationTests.swift | 1 - Tests/BuildTests/BuildOperationTests.swift | 2 - Tests/BuildTests/BuildPlanTests.swift | 1 - .../CrossCompilationBuildPlanTests.swift | 3 - .../LLBuildManifestBuilderTests.swift | 1 - Tests/CommandsTests/APIDiffTests.swift | 1 - Tests/CommandsTests/BuildCommandTests.swift | 1 - Tests/CommandsTests/PackageCommandTests.swift | 1 - .../SwiftCommandStateTests.swift | 1 - .../SwiftSDKBundleTests.swift | 1 - 86 files changed, 283 insertions(+), 415 deletions(-) diff --git a/Sources/Basics/ProgressAnimation/NinjaProgressAnimation.swift b/Sources/Basics/ProgressAnimation/NinjaProgressAnimation.swift index 5a42adec7c0..33d2e121957 100644 --- a/Sources/Basics/ProgressAnimation/NinjaProgressAnimation.swift +++ b/Sources/Basics/ProgressAnimation/NinjaProgressAnimation.swift @@ -15,8 +15,7 @@ import protocol TSCBasic.WritableByteStream extension ProgressAnimation { /// A ninja-like progress animation that adapts to the provided output stream. - @_spi(SwiftPMInternal) - public static func ninja( + package static func ninja( stream: WritableByteStream, verbose: Bool ) -> any ProgressAnimationProtocol { diff --git a/Sources/Basics/ProgressAnimation/PercentProgressAnimation.swift b/Sources/Basics/ProgressAnimation/PercentProgressAnimation.swift index 0020b10df86..a0929634288 100644 --- a/Sources/Basics/ProgressAnimation/PercentProgressAnimation.swift +++ b/Sources/Basics/ProgressAnimation/PercentProgressAnimation.swift @@ -15,8 +15,7 @@ import protocol TSCBasic.WritableByteStream extension ProgressAnimation { /// A percent-based progress animation that adapts to the provided output stream. - @_spi(SwiftPMInternal) - public static func percent( + package static func percent( stream: WritableByteStream, verbose: Bool, header: String diff --git a/Sources/Basics/ProgressAnimation/ProgressAnimationProtocol.swift b/Sources/Basics/ProgressAnimation/ProgressAnimationProtocol.swift index 2a596271c8c..43d279cbb46 100644 --- a/Sources/Basics/ProgressAnimation/ProgressAnimationProtocol.swift +++ b/Sources/Basics/ProgressAnimation/ProgressAnimationProtocol.swift @@ -15,12 +15,10 @@ import class TSCBasic.LocalFileOutputByteStream import protocol TSCBasic.WritableByteStream import protocol TSCUtility.ProgressAnimationProtocol -@_spi(SwiftPMInternal) -public typealias ProgressAnimationProtocol = TSCUtility.ProgressAnimationProtocol +package typealias ProgressAnimationProtocol = TSCUtility.ProgressAnimationProtocol /// Namespace to nest public progress animations under. -@_spi(SwiftPMInternal) -public enum ProgressAnimation { +package enum ProgressAnimation { /// Dynamically create a progress animation based on the current stream /// capabilities and desired verbosity. /// diff --git a/Sources/Basics/ProgressAnimation/ThrottledProgressAnimation.swift b/Sources/Basics/ProgressAnimation/ThrottledProgressAnimation.swift index 30f006c873a..4e4747be2dc 100644 --- a/Sources/Basics/ProgressAnimation/ThrottledProgressAnimation.swift +++ b/Sources/Basics/ProgressAnimation/ThrottledProgressAnimation.swift @@ -56,10 +56,8 @@ final class ThrottledProgressAnimation: ProgressAnimationProtocol { } } -@_spi(SwiftPMInternal) extension ProgressAnimationProtocol { - @_spi(SwiftPMInternal) - public func throttled( + package func throttled( now: @escaping () -> C.Instant, interval: C.Duration, clock: C.Type = C.self @@ -67,16 +65,14 @@ extension ProgressAnimationProtocol { ThrottledProgressAnimation(self, now: now, interval: interval, clock: clock) } - @_spi(SwiftPMInternal) - public func throttled( + package func throttled( clock: C, interval: C.Duration ) -> some ProgressAnimationProtocol { self.throttled(now: { clock.now }, interval: interval, clock: C.self) } - @_spi(SwiftPMInternal) - public func throttled( + package func throttled( interval: ContinuousClock.Duration ) -> some ProgressAnimationProtocol { self.throttled(clock: ContinuousClock(), interval: interval) diff --git a/Sources/Build/BuildDescription/ProductBuildDescription.swift b/Sources/Build/BuildDescription/ProductBuildDescription.swift index efdc497f14b..dd0bd9024db 100644 --- a/Sources/Build/BuildDescription/ProductBuildDescription.swift +++ b/Sources/Build/BuildDescription/ProductBuildDescription.swift @@ -13,7 +13,6 @@ import Basics import PackageGraph -@_spi(SwiftPMInternal) import PackageModel import OrderedCollections diff --git a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift index 184ab64f59b..f65c1439d41 100644 --- a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift @@ -17,7 +17,6 @@ import PackageGraph import PackageLoading import PackageModel -@_spi(SwiftPMInternal) import SPMBuildCore #if USE_IMPL_ONLY_IMPORTS diff --git a/Sources/Build/BuildOperation.swift b/Sources/Build/BuildOperation.swift index ca959a84d18..58b5bf2a74d 100644 --- a/Sources/Build/BuildOperation.swift +++ b/Sources/Build/BuildOperation.swift @@ -10,10 +10,8 @@ // //===----------------------------------------------------------------------===// -@_spi(SwiftPMInternal) import Basics -@_spi(SwiftPMInternal) import Build import LLBuildManifest @@ -21,7 +19,6 @@ import PackageGraph import PackageLoading import PackageModel -@_spi(SwiftPMInternal) import SPMBuildCore import SPMLLBuild @@ -43,8 +40,7 @@ import DriverSupport import SwiftDriver #endif -@_spi(SwiftPMInternal) -public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildSystem, BuildErrorAdviceProvider { +package final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildSystem, BuildErrorAdviceProvider { /// The delegate used by the build system. public weak var delegate: SPMBuildCore.BuildSystemDelegate? diff --git a/Sources/Build/BuildOperationBuildSystemDelegateHandler.swift b/Sources/Build/BuildOperationBuildSystemDelegateHandler.swift index e9814f2e917..dc1cdba734b 100644 --- a/Sources/Build/BuildOperationBuildSystemDelegateHandler.swift +++ b/Sources/Build/BuildOperationBuildSystemDelegateHandler.swift @@ -10,14 +10,12 @@ // //===----------------------------------------------------------------------===// -@_spi(SwiftPMInternal) import Basics import Dispatch import Foundation import LLBuildManifest import PackageModel -@_spi(SwiftPMInternal) import SPMBuildCore import SPMLLBuild diff --git a/Sources/Build/BuildPlan/BuildPlan+Product.swift b/Sources/Build/BuildPlan/BuildPlan+Product.swift index e04daac0130..436375f8d80 100644 --- a/Sources/Build/BuildPlan/BuildPlan+Product.swift +++ b/Sources/Build/BuildPlan/BuildPlan+Product.swift @@ -18,7 +18,6 @@ import struct PackageGraph.ResolvedTarget import class PackageModel.BinaryTarget import class PackageModel.ClangTarget -@_spi(SwiftPMInternal) import class PackageModel.Target import class PackageModel.SwiftTarget diff --git a/Sources/Build/BuildPlan/BuildPlan.swift b/Sources/Build/BuildPlan/BuildPlan.swift index 0234675dcf7..f9ef7683c8d 100644 --- a/Sources/Build/BuildPlan/BuildPlan.swift +++ b/Sources/Build/BuildPlan/BuildPlan.swift @@ -225,8 +225,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan { /// source files as well as directories to which any changes should cause us to reevaluate the build plan. public let prebuildCommandResults: [ResolvedTarget.ID: [PrebuildCommandResult]] - @_spi(SwiftPMInternal) - public private(set) var derivedTestTargetsMap: [ResolvedProduct.ID: [ResolvedTarget]] = [:] + package private(set) var derivedTestTargetsMap: [ResolvedProduct.ID: [ResolvedTarget]] = [:] /// Cache for pkgConfig flags. private var pkgConfigCache = [SystemLibraryTarget: (cFlags: [String], libs: [String])]() diff --git a/Sources/Commands/CommandWorkspaceDelegate.swift b/Sources/Commands/CommandWorkspaceDelegate.swift index e5e5983fb0e..38de7358c39 100644 --- a/Sources/Commands/CommandWorkspaceDelegate.swift +++ b/Sources/Commands/CommandWorkspaceDelegate.swift @@ -12,7 +12,6 @@ import Basics -@_spi(SwiftPMInternal) import CoreCommands import Dispatch @@ -215,22 +214,22 @@ final class CommandWorkspaceDelegate: WorkspaceDelegate { } } - public func willUpdateDependencies() { + package func willUpdateDependencies() { self.observabilityScope.emit(debug: "Updating dependencies") os_signpost(.begin, name: SignpostName.updatingDependencies) } - public func didUpdateDependencies(duration: DispatchTimeInterval) { + package func didUpdateDependencies(duration: DispatchTimeInterval) { self.observabilityScope.emit(debug: "Dependencies updated in (\(duration.descriptionInSeconds))") os_signpost(.end, name: SignpostName.updatingDependencies) } - public func willResolveDependencies() { + package func willResolveDependencies() { self.observabilityScope.emit(debug: "Resolving dependencies") os_signpost(.begin, name: SignpostName.resolvingDependencies) } - public func didResolveDependencies(duration: DispatchTimeInterval) { + package func didResolveDependencies(duration: DispatchTimeInterval) { self.observabilityScope.emit(debug: "Dependencies resolved in (\(duration.descriptionInSeconds))") os_signpost(.end, name: SignpostName.resolvingDependencies) } @@ -267,7 +266,7 @@ final class CommandWorkspaceDelegate: WorkspaceDelegate { func willLoadManifest(packageIdentity: PackageIdentity, packagePath: AbsolutePath, url: String, version: Version?, packageKind: PackageReference.Kind) {} } -public extension _SwiftCommand { +package extension _SwiftCommand { var workspaceDelegateProvider: WorkspaceDelegateProvider { return { CommandWorkspaceDelegate( diff --git a/Sources/Commands/PackageCommands/APIDiff.swift b/Sources/Commands/PackageCommands/APIDiff.swift index 8606732616d..079b813aafd 100644 --- a/Sources/Commands/PackageCommands/APIDiff.swift +++ b/Sources/Commands/PackageCommands/APIDiff.swift @@ -13,7 +13,6 @@ import ArgumentParser import Basics -@_spi(SwiftPMInternal) import CoreCommands import Dispatch @@ -21,7 +20,6 @@ import PackageGraph import PackageModel import SourceControl -@_spi(SwiftPMInternal) import SPMBuildCore struct DeprecatedAPIDiff: ParsableCommand { diff --git a/Sources/Commands/PackageCommands/ArchiveSource.swift b/Sources/Commands/PackageCommands/ArchiveSource.swift index 9dd95058c0a..cdd003d1e62 100644 --- a/Sources/Commands/PackageCommands/ArchiveSource.swift +++ b/Sources/Commands/PackageCommands/ArchiveSource.swift @@ -13,7 +13,6 @@ import ArgumentParser import Basics -@_spi(SwiftPMInternal) import CoreCommands import SourceControl @@ -62,7 +61,7 @@ extension SwiftPackageCommand { } } - public static func archiveSource( + package static func archiveSource( at packageDirectory: AbsolutePath, to archivePath: AbsolutePath, fileSystem: FileSystem, diff --git a/Sources/Commands/PackageCommands/CompletionCommand.swift b/Sources/Commands/PackageCommands/CompletionCommand.swift index 6eadce8a34f..021ad4fa9fe 100644 --- a/Sources/Commands/PackageCommands/CompletionCommand.swift +++ b/Sources/Commands/PackageCommands/CompletionCommand.swift @@ -12,7 +12,6 @@ import ArgumentParser -@_spi(SwiftPMInternal) import CoreCommands import var TSCBasic.stdoutStream diff --git a/Sources/Commands/PackageCommands/ComputeChecksum.swift b/Sources/Commands/PackageCommands/ComputeChecksum.swift index 73ca9541d97..cc0f7017d4a 100644 --- a/Sources/Commands/PackageCommands/ComputeChecksum.swift +++ b/Sources/Commands/PackageCommands/ComputeChecksum.swift @@ -13,7 +13,6 @@ import ArgumentParser import Basics -@_spi(SwiftPMInternal) import CoreCommands import Workspace diff --git a/Sources/Commands/PackageCommands/Config.swift b/Sources/Commands/PackageCommands/Config.swift index 83eb1d4d91d..357ddba1d40 100644 --- a/Sources/Commands/PackageCommands/Config.swift +++ b/Sources/Commands/PackageCommands/Config.swift @@ -13,7 +13,6 @@ import ArgumentParser import Basics -@_spi(SwiftPMInternal) import CoreCommands import Workspace diff --git a/Sources/Commands/PackageCommands/Describe.swift b/Sources/Commands/PackageCommands/Describe.swift index d8d283fbb7a..7e06f1e4ded 100644 --- a/Sources/Commands/PackageCommands/Describe.swift +++ b/Sources/Commands/PackageCommands/Describe.swift @@ -13,7 +13,6 @@ import ArgumentParser import Basics -@_spi(SwiftPMInternal) import CoreCommands import Foundation diff --git a/Sources/Commands/PackageCommands/DumpCommands.swift b/Sources/Commands/PackageCommands/DumpCommands.swift index b8f660ba73b..1120058dc04 100644 --- a/Sources/Commands/PackageCommands/DumpCommands.swift +++ b/Sources/Commands/PackageCommands/DumpCommands.swift @@ -13,13 +13,11 @@ import ArgumentParser import Basics -@_spi(SwiftPMInternal) import CoreCommands import Foundation import PackageModel -@_spi(SwiftPMInternal) import SPMBuildCore import XCBuildSupport diff --git a/Sources/Commands/PackageCommands/EditCommands.swift b/Sources/Commands/PackageCommands/EditCommands.swift index a86d3889467..db6a4e8004c 100644 --- a/Sources/Commands/PackageCommands/EditCommands.swift +++ b/Sources/Commands/PackageCommands/EditCommands.swift @@ -13,7 +13,6 @@ import ArgumentParser import Basics -@_spi(SwiftPMInternal) import CoreCommands import SourceControl diff --git a/Sources/Commands/PackageCommands/Format.swift b/Sources/Commands/PackageCommands/Format.swift index dc577594418..39140940005 100644 --- a/Sources/Commands/PackageCommands/Format.swift +++ b/Sources/Commands/PackageCommands/Format.swift @@ -13,7 +13,6 @@ import ArgumentParser import Basics -@_spi(SwiftPMInternal) import CoreCommands import PackageModel diff --git a/Sources/Commands/PackageCommands/Init.swift b/Sources/Commands/PackageCommands/Init.swift index 05090069eea..3daeab93b7c 100644 --- a/Sources/Commands/PackageCommands/Init.swift +++ b/Sources/Commands/PackageCommands/Init.swift @@ -13,7 +13,6 @@ import ArgumentParser import Basics -@_spi(SwiftPMInternal) import CoreCommands import Workspace @@ -21,7 +20,7 @@ import SPMBuildCore extension SwiftPackageCommand { struct Init: SwiftCommand { - public static let configuration = CommandConfiguration( + package static let configuration = CommandConfiguration( abstract: "Initialize a new package") @OptionGroup(visibility: .hidden) diff --git a/Sources/Commands/PackageCommands/InstalledPackages.swift b/Sources/Commands/PackageCommands/InstalledPackages.swift index 0e304e788a8..9deaceab9fd 100644 --- a/Sources/Commands/PackageCommands/InstalledPackages.swift +++ b/Sources/Commands/PackageCommands/InstalledPackages.swift @@ -12,13 +12,11 @@ import ArgumentParser -@_spi(SwiftPMInternal) import CoreCommands import Foundation import PackageModel -@_spi(SwiftPMInternal) import SPMBuildCore import TSCBasic diff --git a/Sources/Commands/PackageCommands/Learn.swift b/Sources/Commands/PackageCommands/Learn.swift index df6bbc758e6..7b0f21e2ff1 100644 --- a/Sources/Commands/PackageCommands/Learn.swift +++ b/Sources/Commands/PackageCommands/Learn.swift @@ -13,7 +13,6 @@ import ArgumentParser import Basics -@_spi(SwiftPMInternal) import CoreCommands import PackageGraph diff --git a/Sources/Commands/PackageCommands/PluginCommand.swift b/Sources/Commands/PackageCommands/PluginCommand.swift index 0c472b05efb..b7620cdec77 100644 --- a/Sources/Commands/PackageCommands/PluginCommand.swift +++ b/Sources/Commands/PackageCommands/PluginCommand.swift @@ -13,10 +13,8 @@ import ArgumentParser import Basics -@_spi(SwiftPMInternal) import CoreCommands -@_spi(SwiftPMInternal) import SPMBuildCore import Dispatch diff --git a/Sources/Commands/PackageCommands/ResetCommands.swift b/Sources/Commands/PackageCommands/ResetCommands.swift index 81b07745524..6711e4840ba 100644 --- a/Sources/Commands/PackageCommands/ResetCommands.swift +++ b/Sources/Commands/PackageCommands/ResetCommands.swift @@ -12,7 +12,6 @@ import ArgumentParser -@_spi(SwiftPMInternal) import CoreCommands extension SwiftPackageCommand { diff --git a/Sources/Commands/PackageCommands/Resolve.swift b/Sources/Commands/PackageCommands/Resolve.swift index bae46153e9f..a940f778f40 100644 --- a/Sources/Commands/PackageCommands/Resolve.swift +++ b/Sources/Commands/PackageCommands/Resolve.swift @@ -12,7 +12,6 @@ import ArgumentParser -@_spi(SwiftPMInternal) import CoreCommands import TSCUtility diff --git a/Sources/Commands/PackageCommands/ShowDependencies.swift b/Sources/Commands/PackageCommands/ShowDependencies.swift index 086b0815b4e..bd65237bb02 100644 --- a/Sources/Commands/PackageCommands/ShowDependencies.swift +++ b/Sources/Commands/PackageCommands/ShowDependencies.swift @@ -14,7 +14,6 @@ import ArgumentParser import Basics -@_spi(SwiftPMInternal) import CoreCommands import PackageGraph @@ -69,7 +68,7 @@ extension SwiftPackageCommand { enum ShowDependenciesMode: String, RawRepresentable, CustomStringConvertible, ExpressibleByArgument { case text, dot, json, flatlist - public init?(rawValue: String) { + package init?(rawValue: String) { switch rawValue.lowercased() { case "text": self = .text @@ -84,7 +83,7 @@ extension SwiftPackageCommand { } } - public var description: String { + package var description: String { switch self { case .text: return "text" case .dot: return "dot" diff --git a/Sources/Commands/PackageCommands/SwiftPackageCommand.swift b/Sources/Commands/PackageCommands/SwiftPackageCommand.swift index b8497dcbc09..ddedbaf7046 100644 --- a/Sources/Commands/PackageCommands/SwiftPackageCommand.swift +++ b/Sources/Commands/PackageCommands/SwiftPackageCommand.swift @@ -13,7 +13,6 @@ import ArgumentParser import Basics -@_spi(SwiftPMInternal) import CoreCommands import Foundation @@ -28,8 +27,8 @@ import XCBuildSupport import enum TSCUtility.Diagnostics /// swift-package tool namespace -public struct SwiftPackageCommand: AsyncParsableCommand { - public static var configuration = CommandConfiguration( +package struct SwiftPackageCommand: AsyncParsableCommand { + package static var configuration = CommandConfiguration( commandName: "package", _superCommandName: "swift", abstract: "Perform operations on Swift packages", @@ -77,9 +76,9 @@ public struct SwiftPackageCommand: AsyncParsableCommand { @OptionGroup() var globalOptions: GlobalOptions - public static var _errorLabel: String { "error" } + package static var _errorLabel: String { "error" } - public init() {} + package init() {} } extension SwiftPackageCommand { diff --git a/Sources/Commands/PackageCommands/ToolsVersionCommand.swift b/Sources/Commands/PackageCommands/ToolsVersionCommand.swift index 8c167a4a8a6..046fc83e415 100644 --- a/Sources/Commands/PackageCommands/ToolsVersionCommand.swift +++ b/Sources/Commands/PackageCommands/ToolsVersionCommand.swift @@ -12,7 +12,6 @@ import ArgumentParser -@_spi(SwiftPMInternal) import CoreCommands import PackageLoading diff --git a/Sources/Commands/PackageCommands/Update.swift b/Sources/Commands/PackageCommands/Update.swift index c222c469d56..b4ced1bad0e 100644 --- a/Sources/Commands/PackageCommands/Update.swift +++ b/Sources/Commands/PackageCommands/Update.swift @@ -12,7 +12,6 @@ import ArgumentParser -@_spi(SwiftPMInternal) import CoreCommands import Dispatch diff --git a/Sources/Commands/Snippets/CardStack.swift b/Sources/Commands/Snippets/CardStack.swift index b9c02a80b77..e17115cfc90 100644 --- a/Sources/Commands/Snippets/CardStack.swift +++ b/Sources/Commands/Snippets/CardStack.swift @@ -12,7 +12,6 @@ import Basics -@_spi(SwiftPMInternal) import CoreCommands import PackageGraph diff --git a/Sources/Commands/Snippets/Cards/SnippetCard.swift b/Sources/Commands/Snippets/Cards/SnippetCard.swift index bf87796d5f9..aabfc2f5025 100644 --- a/Sources/Commands/Snippets/Cards/SnippetCard.swift +++ b/Sources/Commands/Snippets/Cards/SnippetCard.swift @@ -12,11 +12,9 @@ import Basics -@_spi(SwiftPMInternal) import CoreCommands -@_spi(SwiftPMInternal) import SPMBuildCore import PackageModel diff --git a/Sources/Commands/Snippets/Cards/SnippetGroupCard.swift b/Sources/Commands/Snippets/Cards/SnippetGroupCard.swift index 42f21be615c..5b47aaf238c 100644 --- a/Sources/Commands/Snippets/Cards/SnippetGroupCard.swift +++ b/Sources/Commands/Snippets/Cards/SnippetGroupCard.swift @@ -10,7 +10,6 @@ // //===----------------------------------------------------------------------===// -@_spi(SwiftPMInternal) import CoreCommands import PackageModel diff --git a/Sources/Commands/Snippets/Cards/TopCard.swift b/Sources/Commands/Snippets/Cards/TopCard.swift index 89fe006d487..7b915234db0 100644 --- a/Sources/Commands/Snippets/Cards/TopCard.swift +++ b/Sources/Commands/Snippets/Cards/TopCard.swift @@ -11,7 +11,6 @@ //===----------------------------------------------------------------------===// -@_spi(SwiftPMInternal) import CoreCommands import Foundation diff --git a/Sources/Commands/Snippets/Colorful.swift b/Sources/Commands/Snippets/Colorful.swift index 0bf12d9c3b5..23a264e6881 100644 --- a/Sources/Commands/Snippets/Colorful.swift +++ b/Sources/Commands/Snippets/Colorful.swift @@ -61,20 +61,20 @@ extension Optional: Colorful where Wrapped: Colorful { } @resultBuilder -public struct ColorBuilder { - public static func buildOptional(_ component: [Colorful]?) -> [Colorful] { +package struct ColorBuilder { + package static func buildOptional(_ component: [Colorful]?) -> [Colorful] { return component ?? [] } - public static func buildBlock(_ components: Colorful...) -> [Colorful] { + package static func buildBlock(_ components: Colorful...) -> [Colorful] { return components } - public static func buildEither(first component: [Colorful]) -> [Colorful] { + package static func buildEither(first component: [Colorful]) -> [Colorful] { return component } - public static func buildEither(second component: [Colorful]) -> [Colorful] { + package static func buildEither(second component: [Colorful]) -> [Colorful] { return component } } @@ -130,75 +130,75 @@ enum Color4: String, Color { } } -public func colorized(@ColorBuilder builder: () -> [Colorful]) -> Colorful { +package func colorized(@ColorBuilder builder: () -> [Colorful]) -> Colorful { return Colorized(.reset, builder) } -public func plain(@ColorBuilder builder: () -> [Colorful]) -> Colorful { +package func plain(@ColorBuilder builder: () -> [Colorful]) -> Colorful { return Colorized(.reset, builder) } -public func black(@ColorBuilder builder: () -> [Colorful]) -> Colorful { +package func black(@ColorBuilder builder: () -> [Colorful]) -> Colorful { return Colorized(.black, builder) } -public func brightBlack(@ColorBuilder builder: () -> [Colorful]) -> Colorful { +package func brightBlack(@ColorBuilder builder: () -> [Colorful]) -> Colorful { return Colorized(.brightBlack, builder) } -public func red(@ColorBuilder builder: () -> [Colorful]) -> Colorful { +package func red(@ColorBuilder builder: () -> [Colorful]) -> Colorful { return Colorized(.red, builder) } -public func brightRed(@ColorBuilder builder: () -> [Colorful]) -> Colorful { +package func brightRed(@ColorBuilder builder: () -> [Colorful]) -> Colorful { return Colorized(.brightRed, builder) } -public func green(@ColorBuilder builder: () -> [Colorful]) -> Colorful { +package func green(@ColorBuilder builder: () -> [Colorful]) -> Colorful { return Colorized(.green, builder) } -public func brightGreen(@ColorBuilder builder: () -> [Colorful]) -> Colorful { +package func brightGreen(@ColorBuilder builder: () -> [Colorful]) -> Colorful { return Colorized(.brightGreen, builder) } -public func yellow(@ColorBuilder builder: () -> [Colorful]) -> Colorful { +package func yellow(@ColorBuilder builder: () -> [Colorful]) -> Colorful { return Colorized(.yellow, builder) } -public func brightYellow(@ColorBuilder builder: () -> [Colorful]) -> Colorful { +package func brightYellow(@ColorBuilder builder: () -> [Colorful]) -> Colorful { return Colorized(.brightYellow, builder) } -public func blue(@ColorBuilder builder: () -> [Colorful]) -> Colorful { +package func blue(@ColorBuilder builder: () -> [Colorful]) -> Colorful { return Colorized(.blue, builder) } -public func brightBlue(@ColorBuilder builder: () -> [Colorful]) -> Colorful { +package func brightBlue(@ColorBuilder builder: () -> [Colorful]) -> Colorful { return Colorized(.brightBlue, builder) } -public func magenta(@ColorBuilder builder: () -> [Colorful]) -> Colorful { +package func magenta(@ColorBuilder builder: () -> [Colorful]) -> Colorful { return Colorized(.magenta, builder) } -public func brightMagenta(@ColorBuilder builder: () -> [Colorful]) -> Colorful { +package func brightMagenta(@ColorBuilder builder: () -> [Colorful]) -> Colorful { return Colorized(.brightMagenta, builder) } -public func cyan(@ColorBuilder builder: () -> [Colorful]) -> Colorful { +package func cyan(@ColorBuilder builder: () -> [Colorful]) -> Colorful { return Colorized(.cyan, builder) } -public func brightCyan(@ColorBuilder builder: () -> [Colorful]) -> Colorful { +package func brightCyan(@ColorBuilder builder: () -> [Colorful]) -> Colorful { return Colorized(.brightCyan, builder) } -public func white(@ColorBuilder builder: () -> [Colorful]) -> Colorful { +package func white(@ColorBuilder builder: () -> [Colorful]) -> Colorful { return Colorized(.white, builder) } -public func brightWhite(@ColorBuilder builder: () -> [Colorful]) -> Colorful { +package func brightWhite(@ColorBuilder builder: () -> [Colorful]) -> Colorful { return Colorized(.brightWhite, builder) } diff --git a/Sources/Commands/SwiftBuildCommand.swift b/Sources/Commands/SwiftBuildCommand.swift index e67b006865f..e55f533dcce 100644 --- a/Sources/Commands/SwiftBuildCommand.swift +++ b/Sources/Commands/SwiftBuildCommand.swift @@ -13,15 +13,12 @@ import ArgumentParser import Basics -@_spi(SwiftPMInternal) import Build -@_spi(SwiftPMInternal) import CoreCommands import PackageGraph -@_spi(SwiftPMInternal) import SPMBuildCore import XCBuildSupport @@ -95,7 +92,7 @@ struct BuildCommandOptions: ParsableArguments { /// If should link the Swift stdlib statically. @Flag(name: .customLong("static-swift-stdlib"), inversion: .prefixedNo, help: "Link Swift stdlib statically") - public var shouldLinkStaticSwiftStdlib: Bool = false + package var shouldLinkStaticSwiftStdlib: Bool = false /// Which testing libraries to use (and any related options.) @OptionGroup() @@ -114,9 +111,8 @@ struct BuildCommandOptions: ParsableArguments { } /// swift-build command namespace -@_spi(SwiftPMInternal) -public struct SwiftBuildCommand: AsyncSwiftCommand { - public static var configuration = CommandConfiguration( +package struct SwiftBuildCommand: AsyncSwiftCommand { + package static var configuration = CommandConfiguration( commandName: "build", _superCommandName: "swift", abstract: "Build sources into binary products", @@ -125,12 +121,12 @@ public struct SwiftBuildCommand: AsyncSwiftCommand { helpNames: [.short, .long, .customLong("help", withSingleDash: true)]) @OptionGroup() - public var globalOptions: GlobalOptions + package var globalOptions: GlobalOptions @OptionGroup() var options: BuildCommandOptions - public func run(_ swiftCommandState: SwiftCommandState) async throws { + package func run(_ swiftCommandState: SwiftCommandState) async throws { if options.shouldPrintBinPath { return try print(swiftCommandState.productsBuildParameters.buildPath.description) } @@ -183,10 +179,10 @@ public struct SwiftBuildCommand: AsyncSwiftCommand { } } - public init() {} + package init() {} } -public extension _SwiftCommand { +package extension _SwiftCommand { func buildSystemProvider(_ swiftCommandState: SwiftCommandState) throws -> BuildSystemProvider { swiftCommandState.defaultBuildSystemProvider } diff --git a/Sources/Commands/SwiftRunCommand.swift b/Sources/Commands/SwiftRunCommand.swift index c92431cf908..217a5a2268c 100644 --- a/Sources/Commands/SwiftRunCommand.swift +++ b/Sources/Commands/SwiftRunCommand.swift @@ -13,14 +13,12 @@ import ArgumentParser import Basics -@_spi(SwiftPMInternal) import CoreCommands import Foundation import PackageGraph import PackageModel -@_spi(SwiftPMInternal) import SPMBuildCore import enum TSCBasic.ProcessEnv @@ -96,9 +94,8 @@ struct RunCommandOptions: ParsableArguments { } /// swift-run command namespace -@_spi(SwiftPMInternal) -public struct SwiftRunCommand: AsyncSwiftCommand { - public static var configuration = CommandConfiguration( +package struct SwiftRunCommand: AsyncSwiftCommand { + package static var configuration = CommandConfiguration( commandName: "run", _superCommandName: "swift", abstract: "Build and run an executable product", @@ -107,16 +104,16 @@ public struct SwiftRunCommand: AsyncSwiftCommand { helpNames: [.short, .long, .customLong("help", withSingleDash: true)]) @OptionGroup() - public var globalOptions: GlobalOptions + package var globalOptions: GlobalOptions @OptionGroup() var options: RunCommandOptions - public var toolWorkspaceConfiguration: ToolWorkspaceConfiguration { + package var toolWorkspaceConfiguration: ToolWorkspaceConfiguration { return .init(wantsREPLProduct: options.mode == .repl) } - public func run(_ swiftCommandState: SwiftCommandState) async throws { + package func run(_ swiftCommandState: SwiftCommandState) async throws { if options.shouldBuildTests && options.shouldSkipBuild { swiftCommandState.observabilityScope.emit( .mutuallyExclusiveArgumentsError(arguments: ["--build-tests", "--skip-build"]) @@ -310,7 +307,7 @@ public struct SwiftRunCommand: AsyncSwiftCommand { try TSCBasic.exec(path: path, args: args) } - public init() {} + package init() {} } private extension Basics.Diagnostic { diff --git a/Sources/Commands/SwiftTestCommand.swift b/Sources/Commands/SwiftTestCommand.swift index 6d62db48a93..0297a4c36a1 100644 --- a/Sources/Commands/SwiftTestCommand.swift +++ b/Sources/Commands/SwiftTestCommand.swift @@ -12,10 +12,8 @@ import ArgumentParser -@_spi(SwiftPMInternal) import Basics -@_spi(SwiftPMInternal) import CoreCommands import Dispatch @@ -23,7 +21,6 @@ import Foundation import PackageGraph import PackageModel -@_spi(SwiftPMInternal) import SPMBuildCore import func TSCLibc.exit @@ -147,7 +144,7 @@ struct TestCommandOptions: ParsableArguments { /// Configure test output. @Option(help: ArgumentHelp("", visibility: .hidden)) - public var testOutput: TestOutput = .default + package var testOutput: TestOutput = .default var enableExperimentalTestOutput: Bool { return testOutput == .experimentalSummary @@ -155,7 +152,7 @@ struct TestCommandOptions: ParsableArguments { } /// Tests filtering specifier, which is used to filter tests to run. -public enum TestCaseSpecifier { +package enum TestCaseSpecifier { /// No filtering case none @@ -170,7 +167,7 @@ public enum TestCaseSpecifier { } /// Different styles of test output. -public enum TestOutput: String, ExpressibleByArgument { +package enum TestOutput: String, ExpressibleByArgument { /// Whatever `xctest` emits to the console. case `default` @@ -182,9 +179,8 @@ public enum TestOutput: String, ExpressibleByArgument { } /// swift-test tool namespace -@_spi(SwiftPMInternal) -public struct SwiftTestCommand: SwiftCommand { - public static var configuration = CommandConfiguration( +package struct SwiftTestCommand: SwiftCommand { + package static var configuration = CommandConfiguration( commandName: "test", _superCommandName: "swift", abstract: "Build and run tests", @@ -195,7 +191,7 @@ public struct SwiftTestCommand: SwiftCommand { ], helpNames: [.short, .long, .customLong("help", withSingleDash: true)]) - public var globalOptions: GlobalOptions { + package var globalOptions: GlobalOptions { options.globalOptions } @@ -356,7 +352,7 @@ public struct SwiftTestCommand: SwiftCommand { // MARK: - Common implementation - public func run(_ swiftCommandState: SwiftCommandState) throws { + package func run(_ swiftCommandState: SwiftCommandState) throws { do { // Validate commands arguments try self.validateArguments(observabilityScope: swiftCommandState.observabilityScope) @@ -581,7 +577,7 @@ public struct SwiftTestCommand: SwiftCommand { } } - public init() {} + package init() {} } extension SwiftTestCommand { @@ -806,7 +802,7 @@ final class TestRunner { /// Executes and returns execution status. Prints test output on standard streams if requested /// - Returns: Boolean indicating if test execution returned code 0, and the output stream result - public func test(outputHandler: @escaping (String) -> Void) -> Bool { + package func test(outputHandler: @escaping (String) -> Void) -> Bool { var success = true for path in self.bundlePaths { let testSuccess = self.test(at: path, outputHandler: outputHandler) diff --git a/Sources/Commands/Utilities/APIDigester.swift b/Sources/Commands/Utilities/APIDigester.swift index e229a775718..91aeab7cf65 100644 --- a/Sources/Commands/Utilities/APIDigester.swift +++ b/Sources/Commands/Utilities/APIDigester.swift @@ -13,12 +13,10 @@ import Dispatch import Foundation -@_spi(SwiftPMInternal) import SPMBuildCore import Basics -@_spi(SwiftPMInternal) import CoreCommands import PackageGraph @@ -185,7 +183,7 @@ struct APIDigesterBaselineDumper { } /// A wrapper for the swift-api-digester tool. -public struct SwiftAPIDigester { +package struct SwiftAPIDigester { /// The file system to use let fileSystem: FileSystem @@ -198,7 +196,7 @@ public struct SwiftAPIDigester { } /// Emit an API baseline file for the specified module at the specified location. - public func emitAPIBaseline( + package func emitAPIBaseline( to outputPath: AbsolutePath, for module: String, buildPlan: SPMBuildCore.BuildPlan @@ -228,7 +226,7 @@ public struct SwiftAPIDigester { } /// Compare the current package API to a provided baseline file. - public func compareAPIToBaseline( + package func compareAPIToBaseline( at baselinePath: AbsolutePath, for module: String, buildPlan: SPMBuildCore.BuildPlan, @@ -273,12 +271,12 @@ public struct SwiftAPIDigester { } extension SwiftAPIDigester { - public enum Error: Swift.Error, CustomStringConvertible { + package enum Error: Swift.Error, CustomStringConvertible { case failedToGenerateBaseline(module: String) case failedToValidateBaseline(module: String) case noSymbolsInBaseline(module: String, toolOutput: String) - public var description: String { + package var description: String { switch self { case .failedToGenerateBaseline(let module): return "failed to generate baseline for \(module)" @@ -293,7 +291,7 @@ extension SwiftAPIDigester { extension SwiftAPIDigester { /// The result of comparing a module's API to a provided baseline. - public struct ComparisonResult { + package struct ComparisonResult { /// The name of the module being diffed. var moduleName: String /// Breaking changes made to the API since the baseline was generated. diff --git a/Sources/Commands/Utilities/MultiRootSupport.swift b/Sources/Commands/Utilities/MultiRootSupport.swift index 7ee468a81f5..7fca57136ca 100644 --- a/Sources/Commands/Utilities/MultiRootSupport.swift +++ b/Sources/Commands/Utilities/MultiRootSupport.swift @@ -21,7 +21,7 @@ import class PackageModel.Manifest /// A bare minimum loader for Xcode workspaces. /// /// Warning: This is only useful for debugging workspaces that contain Swift packages. -public struct XcodeWorkspaceLoader: WorkspaceLoader { +package struct XcodeWorkspaceLoader: WorkspaceLoader { /// The parsed location. private struct Location { @@ -38,13 +38,13 @@ public struct XcodeWorkspaceLoader: WorkspaceLoader { private let fileSystem: FileSystem private let observabilityScope: ObservabilityScope - public init(fileSystem: FileSystem, observabilityScope: ObservabilityScope) { + package init(fileSystem: FileSystem, observabilityScope: ObservabilityScope) { self.fileSystem = fileSystem self.observabilityScope = observabilityScope } /// Load the given workspace and return the file ref paths from it. - public func load(workspace: AbsolutePath) throws -> [AbsolutePath] { + package func load(workspace: AbsolutePath) throws -> [AbsolutePath] { let path = workspace.appending("contents.xcworkspacedata") let contents: Data = try self.fileSystem.readFileContents(path) diff --git a/Sources/Commands/Utilities/PluginDelegate.swift b/Sources/Commands/Utilities/PluginDelegate.swift index 962786756e9..0b68adf360b 100644 --- a/Sources/Commands/Utilities/PluginDelegate.swift +++ b/Sources/Commands/Utilities/PluginDelegate.swift @@ -12,13 +12,11 @@ import Basics -@_spi(SwiftPMInternal) import CoreCommands import Foundation import PackageModel -@_spi(SwiftPMInternal) import SPMBuildCore import protocol TSCBasic.OutputByteStream @@ -80,7 +78,7 @@ final class PluginDelegate: PluginInvocationDelegate { class TeeOutputByteStream: OutputByteStream { var downstreams: [OutputByteStream] - public init(_ downstreams: [OutputByteStream]) { + package init(_ downstreams: [OutputByteStream]) { self.downstreams = downstreams } @@ -88,7 +86,7 @@ final class PluginDelegate: PluginInvocationDelegate { return 0 // should be related to the downstreams somehow } - public func write(_ byte: UInt8) { + package func write(_ byte: UInt8) { for downstream in downstreams { downstream.write(byte) } @@ -100,13 +98,13 @@ final class PluginDelegate: PluginInvocationDelegate { } } - public func flush() { + package func flush() { for downstream in downstreams { downstream.flush() } } - public func addStream(_ stream: OutputByteStream) { + package func addStream(_ stream: OutputByteStream) { self.downstreams.append(stream) } } diff --git a/Sources/Commands/Utilities/SymbolGraphExtract.swift b/Sources/Commands/Utilities/SymbolGraphExtract.swift index f9423384c9b..4d3ad975db2 100644 --- a/Sources/Commands/Utilities/SymbolGraphExtract.swift +++ b/Sources/Commands/Utilities/SymbolGraphExtract.swift @@ -26,7 +26,7 @@ import class TSCBasic.Process import struct TSCBasic.ProcessResult /// A wrapper for swift-symbolgraph-extract tool. -public struct SymbolGraphExtract { +package struct SymbolGraphExtract { let fileSystem: FileSystem let tool: AbsolutePath let observabilityScope: ObservabilityScope @@ -39,13 +39,13 @@ public struct SymbolGraphExtract { var outputFormat = OutputFormat.json(pretty: false) /// Access control levels. - public enum AccessLevel: String, RawRepresentable, CaseIterable, ExpressibleByArgument { + package enum AccessLevel: String, RawRepresentable, CaseIterable, ExpressibleByArgument { // The cases reflect those found in `include/swift/AST/AttrKind.h` of the swift compiler (at commit 03f55d7bb4204ca54841218eb7cc175ae798e3bd) case `private`, `fileprivate`, `internal`, `public`, `open` } /// Output format of the generated symbol graph. - public enum OutputFormat { + package enum OutputFormat { /// JSON format, optionally "pretty-printed" be more human-readable. case json(pretty: Bool) } @@ -53,7 +53,7 @@ public struct SymbolGraphExtract { /// Creates a symbol graph for `target` in `outputDirectory` using the build information from `buildPlan`. /// The `outputDirection` determines how the output from the tool subprocess is handled, and `verbosity` specifies /// how much console output to ask the tool to emit. - public func extractSymbolGraph( + package func extractSymbolGraph( target: ResolvedTarget, buildPlan: BuildPlan, outputRedirection: TSCBasic.Process.OutputRedirection = .none, diff --git a/Sources/Commands/Utilities/TestingSupport.swift b/Sources/Commands/Utilities/TestingSupport.swift index 9d1d15b93cf..43f889fc309 100644 --- a/Sources/Commands/Utilities/TestingSupport.swift +++ b/Sources/Commands/Utilities/TestingSupport.swift @@ -12,7 +12,6 @@ import Basics -@_spi(SwiftPMInternal) import CoreCommands import PackageModel diff --git a/Sources/CoreCommands/BuildSystemSupport.swift b/Sources/CoreCommands/BuildSystemSupport.swift index 5471d924e3e..da932c387af 100644 --- a/Sources/CoreCommands/BuildSystemSupport.swift +++ b/Sources/CoreCommands/BuildSystemSupport.swift @@ -10,13 +10,10 @@ // //===----------------------------------------------------------------------===// -@_spi(SwiftPMInternal) import Build -@_spi(SwiftPMInternal) import SPMBuildCore -@_spi(SwiftPMInternal) import XCBuildSupport import class Basics.ObservabilityScope @@ -94,8 +91,7 @@ private struct XcodeBuildSystemFactory: BuildSystemFactory { } extension SwiftCommandState { - @_spi(SwiftPMInternal) - public var defaultBuildSystemProvider: BuildSystemProvider { + package var defaultBuildSystemProvider: BuildSystemProvider { .init(providers: [ .native: NativeBuildSystemFactory(swiftCommandState: self), .xcode: XcodeBuildSystemFactory(swiftCommandState: self) diff --git a/Sources/CoreCommands/Options.swift b/Sources/CoreCommands/Options.swift index ec54d23d6c4..f7070eba34f 100644 --- a/Sources/CoreCommands/Options.swift +++ b/Sources/CoreCommands/Options.swift @@ -27,7 +27,6 @@ import class PackageModel.Manifest import enum PackageModel.Sanitizer import struct SPMBuildCore.BuildParameters -@_spi(SwiftPMInternal) import struct SPMBuildCore.BuildSystemProvider import struct TSCBasic.StringError @@ -37,58 +36,57 @@ import struct TSCUtility.Version import class Workspace.Workspace import struct Workspace.WorkspaceConfiguration -@_spi(SwiftPMInternal) -public struct GlobalOptions: ParsableArguments { - public init() {} +package struct GlobalOptions: ParsableArguments { + package init() {} @OptionGroup() - public var locations: LocationOptions + package var locations: LocationOptions @OptionGroup() - public var caching: CachingOptions + package var caching: CachingOptions @OptionGroup() - public var logging: LoggingOptions + package var logging: LoggingOptions @OptionGroup() - public var security: SecurityOptions + package var security: SecurityOptions @OptionGroup() - public var resolver: ResolverOptions + package var resolver: ResolverOptions @OptionGroup() - public var build: BuildOptions + package var build: BuildOptions @OptionGroup() - public var linker: LinkerOptions + package var linker: LinkerOptions } -public struct LocationOptions: ParsableArguments { - public init() {} +package struct LocationOptions: ParsableArguments { + package init() {} @Option( name: .customLong("package-path"), help: "Specify the package path to operate on (default current directory). This changes the working directory before any other operation", completion: .directory ) - public var packageDirectory: AbsolutePath? + package var packageDirectory: AbsolutePath? @Option(name: .customLong("cache-path"), help: "Specify the shared cache directory path", completion: .directory) - public var cacheDirectory: AbsolutePath? + package var cacheDirectory: AbsolutePath? @Option( name: .customLong("config-path"), help: "Specify the shared configuration directory path", completion: .directory ) - public var configurationDirectory: AbsolutePath? + package var configurationDirectory: AbsolutePath? @Option( name: .customLong("security-path"), help: "Specify the shared security directory path", completion: .directory ) - public var securityDirectory: AbsolutePath? + package var securityDirectory: AbsolutePath? /// The custom .build directory, if provided. @Option( @@ -107,15 +105,15 @@ public struct LocationOptions: ParsableArguments { /// The path to the file containing multiroot package data. This is currently Xcode's workspace file. @Option(name: .customLong("multiroot-data-file"), help: .hidden, completion: .directory) - public var multirootPackageDataFile: AbsolutePath? + package var multirootPackageDataFile: AbsolutePath? /// Path to the compilation destination describing JSON file. @Option(name: .customLong("destination"), help: .hidden, completion: .directory) - public var customCompileDestination: AbsolutePath? + package var customCompileDestination: AbsolutePath? /// Path to the directory containing installed Swift SDKs. @Option(name: .customLong("experimental-swift-sdks-path"), help: .hidden, completion: .directory) - public var swiftSDKsDirectory: AbsolutePath? + package var swiftSDKsDirectory: AbsolutePath? @Option( name: .customLong("pkg-config-path"), @@ -126,14 +124,14 @@ public struct LocationOptions: ParsableArguments { """, completion: .directory ) - public var pkgConfigDirectories: [AbsolutePath] = [] + package var pkgConfigDirectories: [AbsolutePath] = [] @Option(name: .customLong("ignore-lock"), help: .hidden) - public var ignoreLock: Bool = false + package var ignoreLock: Bool = false } -public struct CachingOptions: ParsableArguments { - public init() {} +package struct CachingOptions: ParsableArguments { + package init() {} /// Disables package caching. @Flag( @@ -141,60 +139,60 @@ public struct CachingOptions: ParsableArguments { inversion: .prefixedEnableDisable, help: "Use a shared cache when fetching dependencies" ) - public var useDependenciesCache: Bool = true + package var useDependenciesCache: Bool = true /// Disables manifest caching. @Flag(name: .customLong("disable-package-manifest-caching"), help: .hidden) - public var shouldDisableManifestCaching: Bool = false + package var shouldDisableManifestCaching: Bool = false /// Whether to enable llbuild manifest caching. @Flag(name: .customLong("build-manifest-caching"), inversion: .prefixedEnableDisable) - public var cacheBuildManifest: Bool = true + package var cacheBuildManifest: Bool = true /// Disables manifest caching. @Option( name: .customLong("manifest-cache"), help: "Caching mode of Package.swift manifests (shared: shared cache, local: package's build directory, none: disabled" ) - public var manifestCachingMode: ManifestCachingMode = .shared + package var manifestCachingMode: ManifestCachingMode = .shared - public enum ManifestCachingMode: String, ExpressibleByArgument { + package enum ManifestCachingMode: String, ExpressibleByArgument { case none case local case shared - public init?(argument: String) { + package init?(argument: String) { self.init(rawValue: argument) } } } -public struct LoggingOptions: ParsableArguments { - public init() {} +package struct LoggingOptions: ParsableArguments { + package init() {} /// The verbosity of informational output. @Flag(name: .shortAndLong, help: "Increase verbosity to include informational output") - public var verbose: Bool = false + package var verbose: Bool = false /// The verbosity of informational output. @Flag(name: [.long, .customLong("vv")], help: "Increase verbosity to include debug output") - public var veryVerbose: Bool = false + package var veryVerbose: Bool = false /// Whether logging output should be limited to `.error`. @Flag(name: .shortAndLong, help: "Decrease verbosity to only include error output.") - public var quiet: Bool = false + package var quiet: Bool = false } -public struct SecurityOptions: ParsableArguments { - public init() {} +package struct SecurityOptions: ParsableArguments { + package init() {} /// Disables sandboxing when executing subprocesses. @Flag(name: .customLong("disable-sandbox"), help: "Disable using the sandbox when executing subprocesses") - public var shouldDisableSandbox: Bool = false + package var shouldDisableSandbox: Bool = false /// Force usage of the netrc file even in cases where it is not allowed. @Flag(name: .customLong("netrc"), help: "Use netrc file even in cases where other credential stores are preferred") - public var forceNetrc: Bool = false + package var forceNetrc: Bool = false /// Whether to load netrc files for authenticating with remote servers /// when downloading binary artifacts. This has no effects on registry @@ -204,7 +202,7 @@ public struct SecurityOptions: ParsableArguments { exclusivity: .exclusive, help: "Load credentials from a netrc file" ) - public var netrc: Bool = true + package var netrc: Bool = true /// The path to the netrc file used when `netrc` is `true`. @Option( @@ -212,7 +210,7 @@ public struct SecurityOptions: ParsableArguments { help: "Specify the netrc file path", completion: .file() ) - public var netrcFilePath: AbsolutePath? + package var netrcFilePath: AbsolutePath? /// Whether to use keychain for authenticating with remote servers /// when downloading binary artifacts. This has no effects on registry @@ -223,61 +221,61 @@ public struct SecurityOptions: ParsableArguments { exclusivity: .exclusive, help: "Search credentials in macOS keychain" ) - public var keychain: Bool = true + package var keychain: Bool = true #else @Flag( inversion: .prefixedEnableDisable, exclusivity: .exclusive, help: .hidden ) - public var keychain: Bool = false + package var keychain: Bool = false #endif @Option(name: .customLong("resolver-fingerprint-checking")) - public var fingerprintCheckingMode: WorkspaceConfiguration.CheckingMode = .strict + package var fingerprintCheckingMode: WorkspaceConfiguration.CheckingMode = .strict @Option(name: .customLong("resolver-signing-entity-checking")) - public var signingEntityCheckingMode: WorkspaceConfiguration.CheckingMode = .warn + package var signingEntityCheckingMode: WorkspaceConfiguration.CheckingMode = .warn @Flag( inversion: .prefixedEnableDisable, exclusivity: .exclusive, help: "Validate signature of a signed package release downloaded from registry" ) - public var signatureValidation: Bool = true + package var signatureValidation: Bool = true } -public struct ResolverOptions: ParsableArguments { - public init() {} +package struct ResolverOptions: ParsableArguments { + package init() {} /// Enable prefetching in resolver which will kick off parallel git cloning. @Flag(name: .customLong("prefetching"), inversion: .prefixedEnableDisable) - public var shouldEnableResolverPrefetching: Bool = true + package var shouldEnableResolverPrefetching: Bool = true /// Use Package.resolved file for resolving dependencies. @Flag( name: [.long, .customLong("disable-automatic-resolution"), .customLong("only-use-versions-from-resolved-file")], help: "Only use versions from the Package.resolved file and fail resolution if it is out-of-date" ) - public var forceResolvedVersions: Bool = false + package var forceResolvedVersions: Bool = false /// Skip updating dependencies from their remote during a resolution. @Flag(name: .customLong("skip-update"), help: "Skip updating dependencies from their remote during a resolution") - public var skipDependencyUpdate: Bool = false + package var skipDependencyUpdate: Bool = false @Flag(help: "Define automatic transformation of source control based dependencies to registry based ones") - public var sourceControlToRegistryDependencyTransformation: SourceControlToRegistryDependencyTransformation = + package var sourceControlToRegistryDependencyTransformation: SourceControlToRegistryDependencyTransformation = .disabled @Option(help: "Default registry URL to use, instead of the registries.json configuration file") - public var defaultRegistryURL: URL? + package var defaultRegistryURL: URL? - public enum SourceControlToRegistryDependencyTransformation: EnumerableFlag { + package enum SourceControlToRegistryDependencyTransformation: EnumerableFlag { case disabled case identity case swizzle - public static func name(for value: Self) -> NameSpecification { + package static func name(for value: Self) -> NameSpecification { switch value { case .disabled: return .customLong("disable-scm-to-registry-transformation") @@ -288,7 +286,7 @@ public struct ResolverOptions: ParsableArguments { } } - public static func help(for value: SourceControlToRegistryDependencyTransformation) -> ArgumentHelp? { + package static func help(for value: SourceControlToRegistryDependencyTransformation) -> ArgumentHelp? { switch value { case .disabled: return "disable source control to registry transformation" @@ -301,13 +299,12 @@ public struct ResolverOptions: ParsableArguments { } } -@_spi(SwiftPMInternal) -public struct BuildOptions: ParsableArguments { - public init() {} +package struct BuildOptions: ParsableArguments { + package init() {} /// Build configuration. @Option(name: .shortAndLong, help: "Build with configuration") - public var configuration: BuildConfiguration = .debug + package var configuration: BuildConfiguration = .debug @Option( name: .customLong("Xcc", withSingleDash: true), @@ -345,7 +342,7 @@ public struct BuildOptions: ParsableArguments { visibility: .hidden ) ) - public var xcbuildFlags: [String] = [] + package var xcbuildFlags: [String] = [] @Option( name: .customLong("Xbuild-tools-swiftc", withSingleDash: true), @@ -355,7 +352,7 @@ public struct BuildOptions: ParsableArguments { visibility: .hidden ) ) - public var _buildToolsSwiftCFlags: [String] = [] + package var _buildToolsSwiftCFlags: [String] = [] @Option( name: .customLong("Xmanifest", withSingleDash: true), @@ -365,7 +362,7 @@ public struct BuildOptions: ParsableArguments { visibility: .hidden ) ) - public var _deprecated_manifestFlags: [String] = [] + package var _deprecated_manifestFlags: [String] = [] var manifestFlags: [String] { self._deprecated_manifestFlags.isEmpty ? @@ -377,7 +374,7 @@ public struct BuildOptions: ParsableArguments { self._buildToolsSwiftCFlags } - public var buildFlags: BuildFlags { + package var buildFlags: BuildFlags { BuildFlags( cCompilerFlags: self.cCompilerFlags, cxxCompilerFlags: self.cxxCompilerFlags, @@ -389,15 +386,15 @@ public struct BuildOptions: ParsableArguments { /// The compilation destination’s target triple. @Option(name: .customLong("triple"), transform: Triple.init) - public var customCompileTriple: Triple? + package var customCompileTriple: Triple? /// Path to the compilation destination’s SDK. @Option(name: .customLong("sdk")) - public var customCompileSDK: AbsolutePath? + package var customCompileSDK: AbsolutePath? /// Path to the compilation destination’s toolchain. @Option(name: .customLong("toolchain")) - public var customCompileToolchain: AbsolutePath? + package var customCompileToolchain: AbsolutePath? /// The architectures to compile for. @Option( @@ -407,47 +404,47 @@ public struct BuildOptions: ParsableArguments { visibility: .hidden ) ) - public var architectures: [String] = [] + package var architectures: [String] = [] /// Filter for selecting a specific Swift SDK to build with. @Option(name: .customLong("experimental-swift-sdk"), help: .hidden) - public var swiftSDKSelector: String? + package var swiftSDKSelector: String? /// Which compile-time sanitizers should be enabled. @Option( name: .customLong("sanitize"), help: "Turn on runtime checks for erroneous behavior, possible values: \(Sanitizer.formattedValues)" ) - public var sanitizers: [Sanitizer] = [] + package var sanitizers: [Sanitizer] = [] - public var enabledSanitizers: EnabledSanitizers { + package var enabledSanitizers: EnabledSanitizers { EnabledSanitizers(Set(sanitizers)) } @Flag(help: "Enable or disable indexing-while-building feature") - public var indexStoreMode: StoreMode = .autoIndexStore + package var indexStoreMode: StoreMode = .autoIndexStore /// Whether to enable generation of `.swiftinterface`s alongside `.swiftmodule`s. @Flag(name: .customLong("enable-parseable-module-interfaces")) - public var shouldEnableParseableModuleInterfaces: Bool = false + package var shouldEnableParseableModuleInterfaces: Bool = false /// The number of jobs for llbuild to start (aka the number of schedulerLanes) @Option(name: .shortAndLong, help: "The number of jobs to spawn in parallel during the build process") - public var jobs: UInt32? + package var jobs: UInt32? /// Whether to use the integrated Swift driver rather than shelling out /// to a separate process. @Flag() - public var useIntegratedSwiftDriver: Bool = false + package var useIntegratedSwiftDriver: Bool = false /// A flag that indicates this build should check whether targets only import /// their explicitly-declared dependencies @Option() - public var explicitTargetDependencyImportCheck: TargetDependencyImportCheckingMode = .none + package var explicitTargetDependencyImportCheck: TargetDependencyImportCheckingMode = .none /// Whether to use the explicit module build flow (with the integrated driver) @Flag(name: .customLong("experimental-explicit-module-build")) - public var useExplicitModuleBuild: Bool = false + package var useExplicitModuleBuild: Bool = false /// The build system to use. @Option(name: .customLong("build-system")) @@ -455,9 +452,9 @@ public struct BuildOptions: ParsableArguments { /// The Debug Information Format to use. @Option(name: .customLong("debug-info-format", withSingleDash: true)) - public var debugInfoFormat: DebugInfoFormat = .dwarf + package var debugInfoFormat: DebugInfoFormat = .dwarf - public var buildSystem: BuildSystemProvider.Kind { + package var buildSystem: BuildSystemProvider.Kind { #if os(macOS) // Force the Xcode build system if we want to build more than one arch. return self.architectures.count > 1 ? .xcode : self._buildSystem @@ -469,7 +466,7 @@ public struct BuildOptions: ParsableArguments { /// Whether to enable test discovery on platforms without Objective-C runtime. @Flag(help: .hidden) - public var enableTestDiscovery: Bool = false + package var enableTestDiscovery: Bool = false /// Path of test entry point file to use, instead of synthesizing one or using `XCTMain.swift` in the package (if /// present). @@ -478,40 +475,40 @@ public struct BuildOptions: ParsableArguments { name: .customLong("experimental-test-entry-point-path"), help: .hidden ) - public var testEntryPointPath: AbsolutePath? + package var testEntryPointPath: AbsolutePath? /// The lto mode to use if any. @Option( name: .customLong("experimental-lto-mode"), help: .hidden ) - public var linkTimeOptimizationMode: LinkTimeOptimizationMode? + package var linkTimeOptimizationMode: LinkTimeOptimizationMode? @Flag(inversion: .prefixedEnableDisable, help: .hidden) - public var getTaskAllowEntitlement: Bool? = nil + package var getTaskAllowEntitlement: Bool? = nil // Whether to omit frame pointers // this can be removed once the backtracer uses DWARF instead of frame pointers @Flag(inversion: .prefixedNo, help: .hidden) - public var omitFramePointers: Bool? = nil + package var omitFramePointers: Bool? = nil // @Flag works best when there is a default value present // if true, false aren't enough and a third state is needed // nil should not be the goto. Instead create an enum - public enum StoreMode: EnumerableFlag { + package enum StoreMode: EnumerableFlag { case autoIndexStore case enableIndexStore case disableIndexStore } - public enum TargetDependencyImportCheckingMode: String, Codable, ExpressibleByArgument { + package enum TargetDependencyImportCheckingMode: String, Codable, ExpressibleByArgument { case none case warn case error } /// See `BuildParameters.LinkTimeOptimizationMode` for details. - public enum LinkTimeOptimizationMode: String, Codable, ExpressibleByArgument { + package enum LinkTimeOptimizationMode: String, Codable, ExpressibleByArgument { /// See `BuildParameters.LinkTimeOptimizationMode.full` for details. case full /// See `BuildParameters.LinkTimeOptimizationMode.thin` for details. @@ -519,7 +516,7 @@ public struct BuildOptions: ParsableArguments { } /// See `BuildParameters.DebugInfoFormat` for details. - public enum DebugInfoFormat: String, Codable, ExpressibleByArgument { + package enum DebugInfoFormat: String, Codable, ExpressibleByArgument { /// See `BuildParameters.DebugInfoFormat.dwarf` for details. case dwarf /// See `BuildParameters.DebugInfoFormat.codeview` for details. @@ -529,25 +526,24 @@ public struct BuildOptions: ParsableArguments { } } -public struct LinkerOptions: ParsableArguments { - public init() {} +package struct LinkerOptions: ParsableArguments { + package init() {} @Flag( name: .customLong("dead-strip"), inversion: .prefixedEnableDisable, help: "Disable/enable dead code stripping by the linker" ) - public var linkerDeadStrip: Bool = true + package var linkerDeadStrip: Bool = true /// Disables adding $ORIGIN/@loader_path to the rpath, useful when deploying @Flag(name: .customLong("disable-local-rpath"), help: "Disable adding $ORIGIN/@loader_path to the rpath by default") - public var shouldDisableLocalRpath: Bool = false + package var shouldDisableLocalRpath: Bool = false } /// Which testing libraries to use (and any related options.) -@_spi(SwiftPMInternal) -public struct TestLibraryOptions: ParsableArguments { - public init() {} +package struct TestLibraryOptions: ParsableArguments { + package init() {} /// Whether to enable support for XCTest (as explicitly specified by the user.) /// @@ -556,10 +552,10 @@ public struct TestLibraryOptions: ParsableArguments { @Flag(name: .customLong("xctest"), inversion: .prefixedEnableDisable, help: "Enable support for XCTest") - public var explicitlyEnableXCTestSupport: Bool? + package var explicitlyEnableXCTestSupport: Bool? /// Whether to enable support for XCTest. - public var enableXCTestSupport: Bool { + package var enableXCTestSupport: Bool { // Default to enabled. explicitlyEnableXCTestSupport ?? true } @@ -572,10 +568,10 @@ public struct TestLibraryOptions: ParsableArguments { @Flag(name: .customLong("experimental-swift-testing"), inversion: .prefixedEnableDisable, help: "Enable experimental support for swift-testing") - public var explicitlyEnableSwiftTestingLibrarySupport: Bool? + package var explicitlyEnableSwiftTestingLibrarySupport: Bool? /// Whether to enable support for swift-testing. - public func enableSwiftTestingLibrarySupport( + package func enableSwiftTestingLibrarySupport( swiftCommandState: SwiftCommandState ) throws -> Bool { // Honor the user's explicit command-line selection, if any. @@ -622,7 +618,7 @@ public struct TestLibraryOptions: ParsableArguments { } /// Get the set of enabled testing libraries. - public func enabledTestingLibraries( + package func enabledTestingLibraries( swiftCommandState: SwiftCommandState ) throws -> Set { var result = Set() diff --git a/Sources/CoreCommands/SwiftCommandObservabilityHandler.swift b/Sources/CoreCommands/SwiftCommandObservabilityHandler.swift index 3536d85240e..56cc2ce9359 100644 --- a/Sources/CoreCommands/SwiftCommandObservabilityHandler.swift +++ b/Sources/CoreCommands/SwiftCommandObservabilityHandler.swift @@ -10,7 +10,6 @@ // //===----------------------------------------------------------------------===// -@_spi(SwiftPMInternal) import Basics import Dispatch @@ -22,10 +21,10 @@ import class TSCUtility.MultiLineNinjaProgressAnimation import class TSCUtility.NinjaProgressAnimation import protocol TSCUtility.ProgressAnimationProtocol -public struct SwiftCommandObservabilityHandler: ObservabilityHandlerProvider { +package struct SwiftCommandObservabilityHandler: ObservabilityHandlerProvider { private let outputHandler: OutputHandler - public var diagnosticsHandler: DiagnosticsHandler { + package var diagnosticsHandler: DiagnosticsHandler { self.outputHandler } @@ -34,7 +33,7 @@ public struct SwiftCommandObservabilityHandler: ObservabilityHandlerProvider { /// - outputStream: an instance of a stream used for output. /// - logLevel: the lowest severity of diagnostics that this handler will forward to `outputStream`. Diagnostics /// emitted below this level will be ignored. - public init(outputStream: OutputByteStream, logLevel: Basics.Diagnostic.Severity) { + package init(outputStream: OutputByteStream, logLevel: Basics.Diagnostic.Severity) { let threadSafeOutputByteStream = outputStream as? ThreadSafeOutputByteStream ?? ThreadSafeOutputByteStream(outputStream) self.outputHandler = OutputHandler(logLevel: logLevel, outputStream: threadSafeOutputByteStream) } @@ -59,7 +58,7 @@ public struct SwiftCommandObservabilityHandler: ObservabilityHandlerProvider { self.outputHandler.prompt(message: message, completion: completion) } - public func wait(timeout: DispatchTime) { + package func wait(timeout: DispatchTime) { self.outputHandler.wait(timeout: timeout) } diff --git a/Sources/CoreCommands/SwiftCommandState.swift b/Sources/CoreCommands/SwiftCommandState.swift index db0aa3e8a38..38c190ec386 100644 --- a/Sources/CoreCommands/SwiftCommandState.swift +++ b/Sources/CoreCommands/SwiftCommandState.swift @@ -18,20 +18,16 @@ import class Foundation.ProcessInfo import PackageGraph import PackageLoading -@_spi(SwiftPMInternal) import PackageModel -@_spi(SwiftPMInternal) import SPMBuildCore import Workspace #if USE_IMPL_ONLY_IMPORTS @_implementationOnly -@_spi(SwiftPMInternal) import DriverSupport #else -@_spi(SwiftPMInternal) import DriverSupport #endif @@ -59,12 +55,12 @@ import TSCUtility // cannot be scoped because of `String.spm_mangleToC99Extended typealias Diagnostic = Basics.Diagnostic -public struct ToolWorkspaceConfiguration { +package struct ToolWorkspaceConfiguration { let shouldInstallSignalHandlers: Bool let wantsMultipleTestProducts: Bool let wantsREPLProduct: Bool - public init( + package init( shouldInstallSignalHandlers: Bool = true, wantsMultipleTestProducts: Bool = false, wantsREPLProduct: Bool = false @@ -75,17 +71,16 @@ public struct ToolWorkspaceConfiguration { } } -public typealias WorkspaceDelegateProvider = ( +package typealias WorkspaceDelegateProvider = ( _ observabilityScope: ObservabilityScope, _ outputHandler: @escaping (String, Bool) -> Void, _ progressHandler: @escaping (Int64, Int64, String?) -> Void, _ inputHandler: @escaping (String, (String?) -> Void) -> Void ) -> WorkspaceDelegate -public typealias WorkspaceLoaderProvider = (_ fileSystem: FileSystem, _ observabilityScope: ObservabilityScope) +package typealias WorkspaceLoaderProvider = (_ fileSystem: FileSystem, _ observabilityScope: ObservabilityScope) -> WorkspaceLoader -@_spi(SwiftPMInternal) -public protocol _SwiftCommand { +package protocol _SwiftCommand { var globalOptions: GlobalOptions { get } var toolWorkspaceConfiguration: ToolWorkspaceConfiguration { get } var workspaceDelegateProvider: WorkspaceDelegateProvider { get } @@ -94,20 +89,19 @@ public protocol _SwiftCommand { } extension _SwiftCommand { - public var toolWorkspaceConfiguration: ToolWorkspaceConfiguration { + package var toolWorkspaceConfiguration: ToolWorkspaceConfiguration { return .init() } } -@_spi(SwiftPMInternal) -public protocol SwiftCommand: ParsableCommand, _SwiftCommand { +package protocol SwiftCommand: ParsableCommand, _SwiftCommand { func run(_ swiftCommandState: SwiftCommandState) throws } extension SwiftCommand { - public static var _errorLabel: String { "error" } + package static var _errorLabel: String { "error" } - public func run() throws { + package func run() throws { let swiftCommandState = try SwiftCommandState( options: globalOptions, toolWorkspaceConfiguration: self.toolWorkspaceConfiguration, @@ -140,16 +134,15 @@ extension SwiftCommand { } } -@_spi(SwiftPMInternal) -public protocol AsyncSwiftCommand: AsyncParsableCommand, _SwiftCommand { +package protocol AsyncSwiftCommand: AsyncParsableCommand, _SwiftCommand { func run(_ swiftCommandState: SwiftCommandState) async throws } extension AsyncSwiftCommand { - public static var _errorLabel: String { "error" } + package static var _errorLabel: String { "error" } // FIXME: It doesn't seem great to have this be duplicated with `SwiftCommand`. - public func run() async throws { + package func run() async throws { let swiftCommandState = try SwiftCommandState( options: globalOptions, toolWorkspaceConfiguration: self.toolWorkspaceConfiguration, @@ -182,24 +175,23 @@ extension AsyncSwiftCommand { } } -@_spi(SwiftPMInternal) -public final class SwiftCommandState { +package final class SwiftCommandState { #if os(Windows) // unfortunately this is needed for C callback handlers used by Windows shutdown handler static var cancellator: Cancellator? #endif /// The original working directory. - public let originalWorkingDirectory: AbsolutePath + package let originalWorkingDirectory: AbsolutePath /// The options of this tool. - public let options: GlobalOptions + package let options: GlobalOptions /// Path to the root package directory, nil if manifest is not found. private let packageRoot: AbsolutePath? /// Helper function to get package root or throw error if it is not found. - public func getPackageRoot() throws -> AbsolutePath { + package func getPackageRoot() throws -> AbsolutePath { guard let packageRoot = packageRoot else { throw StringError("Could not find \(Manifest.filename) in this directory or any of its parent directories.") } @@ -207,7 +199,7 @@ public final class SwiftCommandState { } /// Get the current workspace root object. - public func getWorkspaceRoot() throws -> PackageGraphRootInput { + package func getWorkspaceRoot() throws -> PackageGraphRootInput { let packages: [AbsolutePath] if let workspace = options.locations.multirootPackageDataFile { @@ -221,25 +213,25 @@ public final class SwiftCommandState { } /// Scratch space (.build) directory. - public let scratchDirectory: AbsolutePath + package let scratchDirectory: AbsolutePath /// Path to the shared security directory - public let sharedSecurityDirectory: AbsolutePath + package let sharedSecurityDirectory: AbsolutePath /// Path to the shared cache directory - public let sharedCacheDirectory: AbsolutePath + package let sharedCacheDirectory: AbsolutePath /// Path to the shared configuration directory - public let sharedConfigurationDirectory: AbsolutePath + package let sharedConfigurationDirectory: AbsolutePath /// Path to the cross-compilation Swift SDKs directory. - public let sharedSwiftSDKsDirectory: AbsolutePath + package let sharedSwiftSDKsDirectory: AbsolutePath /// Cancellator to handle cancellation of outstanding work when handling SIGINT - public let cancellator: Cancellator + package let cancellator: Cancellator /// The execution status of the tool. - public var executionStatus: ExecutionStatus = .success + package var executionStatus: ExecutionStatus = .success /// Holds the currently active workspace. /// @@ -252,16 +244,16 @@ public final class SwiftCommandState { private let observabilityHandler: SwiftCommandObservabilityHandler /// The observability scope to emit diagnostics event on - public let observabilityScope: ObservabilityScope + package let observabilityScope: ObservabilityScope /// The min severity at which to log diagnostics - public let logLevel: Basics.Diagnostic.Severity + package let logLevel: Basics.Diagnostic.Severity // should use sandbox on external subcommands - public var shouldDisableSandbox: Bool + package var shouldDisableSandbox: Bool /// The file system in use - public let fileSystem: FileSystem + package let fileSystem: FileSystem /// Provider which can create a `WorkspaceDelegate` if needed. private let workspaceDelegateProvider: WorkspaceDelegateProvider @@ -276,7 +268,7 @@ public final class SwiftCommandState { /// Create an instance of this tool. /// /// - parameter options: The command line options to be passed to this tool. - public convenience init( + package convenience init( options: GlobalOptions, toolWorkspaceConfiguration: ToolWorkspaceConfiguration = .init(), workspaceDelegateProvider: @escaping WorkspaceDelegateProvider, @@ -410,7 +402,7 @@ public final class SwiftCommandState { } /// Returns the currently active workspace. - public func getActiveWorkspace(emitDeprecatedConfigurationWarning: Bool = false) throws -> Workspace { + package func getActiveWorkspace(emitDeprecatedConfigurationWarning: Bool = false) throws -> Workspace { if let workspace = _workspace { return workspace } @@ -471,7 +463,7 @@ public final class SwiftCommandState { return workspace } - public func getRootPackageInformation() throws -> (dependecies: [PackageIdentity: [PackageIdentity]], targets: [PackageIdentity: [String]]) { + package func getRootPackageInformation() throws -> (dependecies: [PackageIdentity: [PackageIdentity]], targets: [PackageIdentity: [String]]) { let workspace = try self.getActiveWorkspace() let root = try self.getWorkspaceRoot() let rootManifests = try temp_await { @@ -538,7 +530,7 @@ public final class SwiftCommandState { } } - public func getAuthorizationProvider() throws -> AuthorizationProvider? { + package func getAuthorizationProvider() throws -> AuthorizationProvider? { var authorization = Workspace.Configuration.Authorization.default if !options.security.netrc { authorization.netrc = .disabled @@ -558,7 +550,7 @@ public final class SwiftCommandState { ) } - public func getRegistryAuthorizationProvider() throws -> AuthorizationProvider? { + package func getRegistryAuthorizationProvider() throws -> AuthorizationProvider? { var authorization = Workspace.Configuration.Authorization.default if let configuredPath = options.security.netrcFilePath { authorization.netrc = .custom(configuredPath) @@ -578,7 +570,7 @@ public final class SwiftCommandState { } /// Resolve the dependencies. - public func resolve() throws { + package func resolve() throws { let workspace = try getActiveWorkspace() let root = try getWorkspaceRoot() @@ -602,7 +594,7 @@ public final class SwiftCommandState { /// - explicitProduct: The product specified on the command line to a “swift run” or “swift build” command. This /// allows executables from dependencies to be run directly without having to hook them up to any particular target. @discardableResult - public func loadPackageGraph( + package func loadPackageGraph( explicitProduct: String? = nil, testEntryPointPath: AbsolutePath? = nil ) throws -> ModulesGraph { @@ -630,7 +622,7 @@ public final class SwiftCommandState { } } - public func getPluginScriptRunner(customPluginsDir: AbsolutePath? = .none) throws -> PluginScriptRunner { + package func getPluginScriptRunner(customPluginsDir: AbsolutePath? = .none) throws -> PluginScriptRunner { let pluginsDir = try customPluginsDir ?? self.getActiveWorkspace().location.pluginWorkingDirectory let cacheDir = pluginsDir.appending("cache") let pluginScriptRunner = try DefaultPluginScriptRunner( @@ -647,11 +639,11 @@ public final class SwiftCommandState { } /// Returns the user toolchain to compile the actual product. - public func getTargetToolchain() throws -> UserToolchain { + package func getTargetToolchain() throws -> UserToolchain { try _targetToolchain.get() } - public func getHostToolchain() throws -> UserToolchain { + package func getHostToolchain() throws -> UserToolchain { try _hostToolchain.get() } @@ -659,7 +651,7 @@ public final class SwiftCommandState { try _manifestLoader.get() } - public func canUseCachedBuildManifest() throws -> Bool { + package func canUseCachedBuildManifest() throws -> Bool { if !self.options.caching.cacheBuildManifest { return false } @@ -688,7 +680,7 @@ public final class SwiftCommandState { // "customOutputStream" is designed to support build output redirection // but it is only expected to be used when invoking builds from "swift build" command. // in all other cases, the build output should go to the default which is stderr - public func createBuildSystem( + package func createBuildSystem( explicitBuildSystem: BuildSystemProvider.Kind? = .none, explicitProduct: String? = .none, cacheBuildManifest: Bool = true, @@ -792,7 +784,7 @@ public final class SwiftCommandState { } /// Return the build parameters for the host toolchain. - public var toolsBuildParameters: BuildParameters { + package var toolsBuildParameters: BuildParameters { get throws { try _toolsBuildParameters.get() } @@ -804,7 +796,7 @@ public final class SwiftCommandState { }) }() - public var productsBuildParameters: BuildParameters { + package var productsBuildParameters: BuildParameters { get throws { try _productsBuildParameters.get() } @@ -912,7 +904,7 @@ public final class SwiftCommandState { }() /// An enum indicating the execution status of run commands. - public enum ExecutionStatus { + package enum ExecutionStatus { case success case failure } @@ -1041,7 +1033,7 @@ extension Basics.Diagnostic { // MARK: - Support for loading external workspaces -public protocol WorkspaceLoader { +package protocol WorkspaceLoader { func load(workspace: AbsolutePath) throws -> [AbsolutePath] } @@ -1050,7 +1042,7 @@ public protocol WorkspaceLoader { extension SwiftCommandState { // FIXME: deprecate these one we are further along refactoring the call sites that use it /// The stream to print standard output on. - public var outputStream: OutputByteStream { + package var outputStream: OutputByteStream { self.observabilityHandler.outputStream } } @@ -1140,7 +1132,7 @@ extension BuildOptions.DebugInfoFormat { } extension Basics.Diagnostic { - public static func mutuallyExclusiveArgumentsError(arguments: [String]) -> Self { + package static func mutuallyExclusiveArgumentsError(arguments: [String]) -> Self { .error(arguments.map { "'\($0)'" }.spm_localizedJoin(type: .conjunction) + " are mutually exclusive") } } diff --git a/Sources/DriverSupport/DriverSupportUtils.swift b/Sources/DriverSupport/DriverSupportUtils.swift index 7f8452ecc10..1586dad73b1 100644 --- a/Sources/DriverSupport/DriverSupportUtils.swift +++ b/Sources/DriverSupport/DriverSupportUtils.swift @@ -84,8 +84,7 @@ public enum DriverSupport { } } - @_spi(SwiftPMInternal) - public static func isPackageNameSupported(toolchain: PackageModel.Toolchain, fileSystem: FileSystem) -> Bool { + package static func isPackageNameSupported(toolchain: PackageModel.Toolchain, fileSystem: FileSystem) -> Bool { DriverSupport.checkToolchainDriverFlags(flags: ["-package-name"], toolchain: toolchain, fileSystem: fileSystem) } } diff --git a/Sources/PackageCollectionsCommand/PackageCollectionsCommand.swift b/Sources/PackageCollectionsCommand/PackageCollectionsCommand.swift index 9af6dd3cbbd..4eb6bb1a644 100644 --- a/Sources/PackageCollectionsCommand/PackageCollectionsCommand.swift +++ b/Sources/PackageCollectionsCommand/PackageCollectionsCommand.swift @@ -13,10 +13,8 @@ import ArgumentParser import Basics -@_spi(SwiftPMInternal) import Commands -@_spi(SwiftPMInternal) import CoreCommands import Foundation @@ -59,8 +57,8 @@ struct JSONOptions: ParsableArguments { var json: Bool = false } -public struct PackageCollectionsCommand: AsyncParsableCommand { - public static var configuration = CommandConfiguration( +package struct PackageCollectionsCommand: AsyncParsableCommand { + package static var configuration = CommandConfiguration( commandName: "package-collection", _superCommandName: "swift", abstract: "Interact with package collections", @@ -77,7 +75,7 @@ public struct PackageCollectionsCommand: AsyncParsableCommand { helpNames: [.short, .long, .customLong("help", withSingleDash: true)] ) - public init() {} + package init() {} // MARK: Collections diff --git a/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift b/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift index d66a2deea5c..44bcbac3664 100644 --- a/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift +++ b/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift @@ -599,8 +599,7 @@ public struct SwiftSDK: Equatable { } /// Computes the target Swift SDK for the given options. - @_spi(SwiftPMInternal) - public static func deriveTargetSwiftSDK( + package static func deriveTargetSwiftSDK( hostSwiftSDK: SwiftSDK, hostTriple: Triple, customCompileDestination: AbsolutePath? = nil, diff --git a/Sources/PackageModel/SwiftSDKs/SwiftSDKBundleStore.swift b/Sources/PackageModel/SwiftSDKs/SwiftSDKBundleStore.swift index 88c911853e2..f5e40bb2083 100644 --- a/Sources/PackageModel/SwiftSDKs/SwiftSDKBundleStore.swift +++ b/Sources/PackageModel/SwiftSDKs/SwiftSDKBundleStore.swift @@ -11,7 +11,6 @@ //===----------------------------------------------------------------------===// // FIXME: can't write `import actor Basics.HTTPClient`, importing the whole module because of that :( -@_spi(SwiftPMInternal) import Basics import struct Foundation.URL import protocol TSCBasic.FileSystem diff --git a/Sources/PackageModel/Target/Target.swift b/Sources/PackageModel/Target/Target.swift index c49b93ba142..6ab22ca7b26 100644 --- a/Sources/PackageModel/Target/Target.swift +++ b/Sources/PackageModel/Target/Target.swift @@ -233,8 +233,7 @@ public class Target: PolymorphicCodableProtocol { /// The build settings assignments of this target. public let buildSettings: BuildSettings.AssignmentTable - @_spi(SwiftPMInternal) - public let buildSettingsDescription: [TargetBuildSettingDescription.Setting] + package let buildSettingsDescription: [TargetBuildSettingDescription.Setting] /// The usages of package plugins by this target. public let pluginUsages: [PluginUsage] @@ -340,8 +339,7 @@ public class Target: PolymorphicCodableProtocol { self.usesUnsafeFlags = try container.decode(Bool.self, forKey: .usesUnsafeFlags) } - @_spi(SwiftPMInternal) - public var isEmbeddedSwiftTarget: Bool { + package var isEmbeddedSwiftTarget: Bool { for case .enableExperimentalFeature("Embedded") in self.buildSettingsDescription.swiftSettings.map(\.kind) { return true } @@ -382,8 +380,7 @@ public extension Sequence where Iterator.Element == Target { } extension [TargetBuildSettingDescription.Setting] { - @_spi(SwiftPMInternal) - public var swiftSettings: Self { + package var swiftSettings: Self { self.filter { $0.tool == .swift } } } diff --git a/Sources/PackageRegistryCommand/PackageRegistryCommand+Auth.swift b/Sources/PackageRegistryCommand/PackageRegistryCommand+Auth.swift index 01079b9dff3..5d55e0c40ab 100644 --- a/Sources/PackageRegistryCommand/PackageRegistryCommand+Auth.swift +++ b/Sources/PackageRegistryCommand/PackageRegistryCommand+Auth.swift @@ -13,10 +13,8 @@ import ArgumentParser import Basics -@_spi(SwiftPMInternal) import Commands -@_spi(SwiftPMInternal) import CoreCommands import Foundation diff --git a/Sources/PackageRegistryCommand/PackageRegistryCommand+Publish.swift b/Sources/PackageRegistryCommand/PackageRegistryCommand+Publish.swift index 2a5458c196c..2ce572df0d9 100644 --- a/Sources/PackageRegistryCommand/PackageRegistryCommand+Publish.swift +++ b/Sources/PackageRegistryCommand/PackageRegistryCommand+Publish.swift @@ -13,10 +13,8 @@ import ArgumentParser import Basics -@_spi(SwiftPMInternal) import Commands -@_spi(SwiftPMInternal) import CoreCommands import Foundation diff --git a/Sources/PackageRegistryCommand/PackageRegistryCommand.swift b/Sources/PackageRegistryCommand/PackageRegistryCommand.swift index 25a8e3bb3ad..19d289b8b07 100644 --- a/Sources/PackageRegistryCommand/PackageRegistryCommand.swift +++ b/Sources/PackageRegistryCommand/PackageRegistryCommand.swift @@ -13,10 +13,8 @@ import ArgumentParser import Basics -@_spi(SwiftPMInternal) import CoreCommands -@_spi(SwiftPMInternal) import Commands import Foundation @@ -24,8 +22,8 @@ import PackageModel import PackageRegistry import Workspace -public struct PackageRegistryCommand: AsyncParsableCommand { - public static var configuration = CommandConfiguration( +package struct PackageRegistryCommand: AsyncParsableCommand { + package static var configuration = CommandConfiguration( commandName: "package-registry", _superCommandName: "swift", abstract: "Interact with package registry and manage related configuration", @@ -44,7 +42,7 @@ public struct PackageRegistryCommand: AsyncParsableCommand { @OptionGroup() var globalOptions: GlobalOptions - public init() {} + package init() {} struct Set: AsyncSwiftCommand { static let configuration = CommandConfiguration( diff --git a/Sources/SPMBuildCore/BuildParameters/BuildParameters+Driver.swift b/Sources/SPMBuildCore/BuildParameters/BuildParameters+Driver.swift index 0355f182cfb..a526bcf3855 100644 --- a/Sources/SPMBuildCore/BuildParameters/BuildParameters+Driver.swift +++ b/Sources/SPMBuildCore/BuildParameters/BuildParameters+Driver.swift @@ -56,7 +56,6 @@ extension BuildParameters { /// Whether the version of Swift Driver used in the currently selected toolchain /// supports `-package-name` options. - @_spi(SwiftPMInternal) - public var isPackageAccessModifierSupported: Bool + package var isPackageAccessModifierSupported: Bool } } diff --git a/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift b/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift index 14349c184bf..4aa3567473d 100644 --- a/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift +++ b/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift @@ -33,8 +33,7 @@ public enum BuildSubset { /// A protocol that represents a build system used by SwiftPM for all build operations. This allows factoring out the /// implementation details between SwiftPM's `BuildOperation` and the XCBuild backed `XCBuildSystem`. -@_spi(SwiftPMInternal) -public protocol BuildSystem: Cancellable { +package protocol BuildSystem: Cancellable { /// The delegate used by the build system. var delegate: BuildSystemDelegate? { get } @@ -115,8 +114,7 @@ extension BuildPlan { } } -@_spi(SwiftPMInternal) -public protocol BuildSystemFactory { +package protocol BuildSystemFactory { func makeBuildSystem( explicitProduct: String?, cacheBuildManifest: Bool, @@ -129,8 +127,7 @@ public protocol BuildSystemFactory { ) throws -> any BuildSystem } -@_spi(SwiftPMInternal) -public struct BuildSystemProvider { +package struct BuildSystemProvider { // TODO: In the future, we may want this to be about specific capabilities of a build system rather than choosing a concrete one. public enum Kind: String, CaseIterable { case native @@ -174,8 +171,7 @@ private enum Errors: Swift.Error { case buildSystemProviderNotRegistered(kind: BuildSystemProvider.Kind) } -@_spi(SwiftPMInternal) -public enum BuildSystemUtilities { +package enum BuildSystemUtilities { /// Returns the build path from the environment, if present. public static func getEnvBuildPath(workingDir: AbsolutePath) throws -> AbsolutePath? { // Don't rely on build path from env for SwiftPM's own tests. diff --git a/Sources/SPMBuildCore/BuildSystem/BuildSystemCommand.swift b/Sources/SPMBuildCore/BuildSystem/BuildSystemCommand.swift index 59bdc266b4d..cc8a13c49e0 100644 --- a/Sources/SPMBuildCore/BuildSystem/BuildSystemCommand.swift +++ b/Sources/SPMBuildCore/BuildSystem/BuildSystemCommand.swift @@ -10,8 +10,7 @@ // //===----------------------------------------------------------------------===// -@_spi(SwiftPMInternal) -public struct BuildSystemCommand: Hashable { +package struct BuildSystemCommand: Hashable { public let name: String public let description: String public let verboseDescription: String? diff --git a/Sources/SPMBuildCore/BuildSystem/BuildSystemDelegate.swift b/Sources/SPMBuildCore/BuildSystem/BuildSystemDelegate.swift index b153190ea1f..9f8a32ecc4d 100644 --- a/Sources/SPMBuildCore/BuildSystem/BuildSystemDelegate.swift +++ b/Sources/SPMBuildCore/BuildSystem/BuildSystemDelegate.swift @@ -13,8 +13,7 @@ import Foundation /// ``BuildSystem`` delegate -@_spi(SwiftPMInternal) -public protocol BuildSystemDelegate: AnyObject { +package protocol BuildSystemDelegate: AnyObject { ///Called when build command is about to start. func buildSystem(_ buildSystem: BuildSystem, willStartCommand command: BuildSystemCommand) @@ -36,7 +35,7 @@ public protocol BuildSystemDelegate: AnyObject { func buildSystemDidCancel(_ buildSystem: BuildSystem) } -public extension BuildSystemDelegate { +extension BuildSystemDelegate { func buildSystem(_ buildSystem: BuildSystem, willStartCommand command: BuildSystemCommand) { } func buildSystem(_ buildSystem: BuildSystem, didStartCommand command: BuildSystemCommand) { } func buildSystem(_ buildSystem: BuildSystem, didUpdateTaskProgress text: String) { } diff --git a/Sources/SPMBuildCore/ResolvedPackage+Extensions.swift b/Sources/SPMBuildCore/ResolvedPackage+Extensions.swift index f4e433b8631..791299fe0bc 100644 --- a/Sources/SPMBuildCore/ResolvedPackage+Extensions.swift +++ b/Sources/SPMBuildCore/ResolvedPackage+Extensions.swift @@ -14,8 +14,7 @@ import struct PackageGraph.ResolvedPackage import struct PackageGraph.ResolvedTarget extension ResolvedPackage { - @_spi(SwiftPMInternal) - public func packageNameArgument(target: ResolvedTarget, isPackageNameSupported: Bool) -> [String] { + package func packageNameArgument(target: ResolvedTarget, isPackageNameSupported: Bool) -> [String] { if self.manifest.usePackageNameFlag, target.packageAccess { ["-package-name", self.identity.description.spm_mangledToC99ExtendedIdentifier()] } else { diff --git a/Sources/SPMBuildCore/Triple+Extensions.swift b/Sources/SPMBuildCore/Triple+Extensions.swift index cdaa1f4cd5c..38da6166597 100644 --- a/Sources/SPMBuildCore/Triple+Extensions.swift +++ b/Sources/SPMBuildCore/Triple+Extensions.swift @@ -23,8 +23,7 @@ extension Triple { } extension Triple { - @_spi(SwiftPMInternal) - public func platformBuildPathComponent(buildSystem: BuildSystemProvider.Kind) -> String { + package func platformBuildPathComponent(buildSystem: BuildSystemProvider.Kind) -> String { // Use "apple" as the subdirectory because in theory Xcode build system // can be used to build for any Apple platform and it has its own // conventions for build subpaths based on platforms. diff --git a/Sources/SPMTestSupport/MockBuildTestHelper.swift b/Sources/SPMTestSupport/MockBuildTestHelper.swift index 38629590d6c..c090266f00a 100644 --- a/Sources/SPMTestSupport/MockBuildTestHelper.swift +++ b/Sources/SPMTestSupport/MockBuildTestHelper.swift @@ -12,7 +12,6 @@ import Basics -@_spi(SwiftPMInternal) import Build import PackageModel diff --git a/Sources/SwiftSDKCommand/Configuration/ConfigureSwiftSDK.swift b/Sources/SwiftSDKCommand/Configuration/ConfigureSwiftSDK.swift index fdf0bd24151..d471356220a 100644 --- a/Sources/SwiftSDKCommand/Configuration/ConfigureSwiftSDK.swift +++ b/Sources/SwiftSDKCommand/Configuration/ConfigureSwiftSDK.swift @@ -12,8 +12,8 @@ import ArgumentParser -public struct ConfigureSwiftSDK: ParsableCommand { - public static let configuration = CommandConfiguration( +package struct ConfigureSwiftSDK: ParsableCommand { + package static let configuration = CommandConfiguration( commandName: "configuration", abstract: """ Manages configuration options for installed Swift SDKs. @@ -25,5 +25,5 @@ public struct ConfigureSwiftSDK: ParsableCommand { ] ) - public init() {} + package init() {} } diff --git a/Sources/SwiftSDKCommand/InstallSwiftSDK.swift b/Sources/SwiftSDKCommand/InstallSwiftSDK.swift index 39b8b4f2471..16162c6a9b1 100644 --- a/Sources/SwiftSDKCommand/InstallSwiftSDK.swift +++ b/Sources/SwiftSDKCommand/InstallSwiftSDK.swift @@ -11,7 +11,6 @@ //===----------------------------------------------------------------------===// import ArgumentParser -@_spi(SwiftPMInternal) import Basics import CoreCommands import Foundation @@ -19,8 +18,8 @@ import PackageModel import var TSCBasic.stdoutStream -public struct InstallSwiftSDK: SwiftSDKSubcommand { - public static let configuration = CommandConfiguration( +package struct InstallSwiftSDK: SwiftSDKSubcommand { + package static let configuration = CommandConfiguration( commandName: "install", abstract: """ Installs a given Swift SDK bundle to a location discoverable by SwiftPM. If the artifact bundle \ @@ -34,7 +33,7 @@ public struct InstallSwiftSDK: SwiftSDKSubcommand { @Argument(help: "A local filesystem path or a URL of a Swift SDK bundle to install.") var bundlePathOrURL: String - public init() {} + package init() {} func run( hostTriple: Triple, diff --git a/Sources/SwiftSDKCommand/ListSwiftSDKs.swift b/Sources/SwiftSDKCommand/ListSwiftSDKs.swift index 23e221788fb..062b43b9e75 100644 --- a/Sources/SwiftSDKCommand/ListSwiftSDKs.swift +++ b/Sources/SwiftSDKCommand/ListSwiftSDKs.swift @@ -16,8 +16,8 @@ import CoreCommands import PackageModel import SPMBuildCore -public struct ListSwiftSDKs: SwiftSDKSubcommand { - public static let configuration = CommandConfiguration( +package struct ListSwiftSDKs: SwiftSDKSubcommand { + package static let configuration = CommandConfiguration( commandName: "list", abstract: """ @@ -28,7 +28,7 @@ public struct ListSwiftSDKs: SwiftSDKSubcommand { @OptionGroup() var locations: LocationOptions - public init() {} + package init() {} func run( hostTriple: Triple, diff --git a/Sources/SwiftSDKCommand/RemoveSwiftSDK.swift b/Sources/SwiftSDKCommand/RemoveSwiftSDK.swift index 2c0a735c257..5e2ef7a1ff1 100644 --- a/Sources/SwiftSDKCommand/RemoveSwiftSDK.swift +++ b/Sources/SwiftSDKCommand/RemoveSwiftSDK.swift @@ -15,8 +15,8 @@ import Basics import CoreCommands import PackageModel -public struct RemoveSwiftSDK: SwiftSDKSubcommand { - public static let configuration = CommandConfiguration( +package struct RemoveSwiftSDK: SwiftSDKSubcommand { + package static let configuration = CommandConfiguration( commandName: "remove", abstract: """ Removes a previously installed Swift SDK bundle from the filesystem. @@ -29,7 +29,7 @@ public struct RemoveSwiftSDK: SwiftSDKSubcommand { @Argument(help: "Name of the Swift SDK bundle or ID of the Swift SDK to remove from the filesystem.") var sdkIDOrBundleName: String - public init() {} + package init() {} func run( hostTriple: Triple, diff --git a/Sources/SwiftSDKCommand/SwiftSDKCommand.swift b/Sources/SwiftSDKCommand/SwiftSDKCommand.swift index 286f34b26af..dc0cf4fe933 100644 --- a/Sources/SwiftSDKCommand/SwiftSDKCommand.swift +++ b/Sources/SwiftSDKCommand/SwiftSDKCommand.swift @@ -13,8 +13,8 @@ import ArgumentParser import Basics -public struct SwiftSDKCommand: AsyncParsableCommand { - public static let configuration = CommandConfiguration( +package struct SwiftSDKCommand: AsyncParsableCommand { + package static let configuration = CommandConfiguration( commandName: "experimental-sdk", _superCommandName: "swift", abstract: "Perform operations on Swift SDKs.", @@ -28,5 +28,5 @@ public struct SwiftSDKCommand: AsyncParsableCommand { helpNames: [.short, .long, .customLong("help", withSingleDash: true)] ) - public init() {} + package init() {} } diff --git a/Sources/SwiftSDKCommand/SwiftSDKSubcommand.swift b/Sources/SwiftSDKCommand/SwiftSDKSubcommand.swift index ce56b9bb553..0f93f6b2274 100644 --- a/Sources/SwiftSDKCommand/SwiftSDKSubcommand.swift +++ b/Sources/SwiftSDKCommand/SwiftSDKSubcommand.swift @@ -54,7 +54,7 @@ extension SwiftSDKSubcommand { return swiftSDKsDirectory } - public func run() async throws { + package func run() async throws { let observabilityHandler = SwiftCommandObservabilityHandler(outputStream: stdoutStream, logLevel: .info) let observabilitySystem = ObservabilitySystem(observabilityHandler) let observabilityScope = observabilitySystem.topScope diff --git a/Sources/XCBuildSupport/PIFBuilder.swift b/Sources/XCBuildSupport/PIFBuilder.swift index 3dda827da8e..153fd73a58a 100644 --- a/Sources/XCBuildSupport/PIFBuilder.swift +++ b/Sources/XCBuildSupport/PIFBuilder.swift @@ -16,7 +16,6 @@ import PackageModel import PackageLoading import PackageGraph -@_spi(SwiftPMInternal) import SPMBuildCore import func TSCBasic.topologicalSort diff --git a/Sources/XCBuildSupport/XCBuildDelegate.swift b/Sources/XCBuildSupport/XCBuildDelegate.swift index cf96f3ef569..6fbbdf48cd1 100644 --- a/Sources/XCBuildSupport/XCBuildDelegate.swift +++ b/Sources/XCBuildSupport/XCBuildDelegate.swift @@ -13,7 +13,6 @@ import Basics import Foundation -@_spi(SwiftPMInternal) import SPMBuildCore import class TSCBasic.ThreadSafeOutputByteStream @@ -22,8 +21,7 @@ import protocol TSCBasic.OutputByteStream import enum TSCUtility.Diagnostics import protocol TSCUtility.ProgressAnimationProtocol -@_spi(SwiftPMInternal) -public class XCBuildDelegate { +package class XCBuildDelegate { private let buildSystem: SPMBuildCore.BuildSystem private var parser: XCBuildOutputParser! private let observabilityScope: ObservabilityScope diff --git a/Sources/XCBuildSupport/XcodeBuildSystem.swift b/Sources/XCBuildSupport/XcodeBuildSystem.swift index 1ba41f012c3..99538864353 100644 --- a/Sources/XCBuildSupport/XcodeBuildSystem.swift +++ b/Sources/XCBuildSupport/XcodeBuildSystem.swift @@ -10,14 +10,12 @@ // //===----------------------------------------------------------------------===// -@_spi(SwiftPMInternal) import Basics import Dispatch import class Foundation.JSONEncoder import PackageGraph import PackageModel -@_spi(SwiftPMInternal) import SPMBuildCore import protocol TSCBasic.OutputByteStream @@ -28,8 +26,7 @@ import func TSCBasic.memoize import enum TSCUtility.Diagnostics -@_spi(SwiftPMInternal) -public final class XcodeBuildSystem: SPMBuildCore.BuildSystem { +package final class XcodeBuildSystem: SPMBuildCore.BuildSystem { private let buildParameters: BuildParameters private let packageGraphLoader: () throws -> ModulesGraph private let logLevel: Basics.Diagnostic.Severity diff --git a/Sources/swift-bootstrap/main.swift b/Sources/swift-bootstrap/main.swift index 1bdd1daf268..6cf38354370 100644 --- a/Sources/swift-bootstrap/main.swift +++ b/Sources/swift-bootstrap/main.swift @@ -13,12 +13,10 @@ import ArgumentParser import Basics -@_spi(SwiftPMInternal) import Build import Dispatch -@_spi(SwiftPMInternal) import DriverSupport import Foundation @@ -27,10 +25,8 @@ import PackageGraph import PackageLoading import PackageModel -@_spi(SwiftPMInternal) import SPMBuildCore -@_spi(SwiftPMInternal) import XCBuildSupport import struct TSCBasic.KeyedPair @@ -52,7 +48,7 @@ struct SwiftBootstrapBuildTool: ParsableCommand { @Option(name: .customLong("package-path"), help: "Specify the package path to operate on (default current directory). This changes the working directory before any other operation", completion: .directory) - public var packageDirectory: AbsolutePath? + package var packageDirectory: AbsolutePath? /// The custom .build directory, if provided. @Option(name: .customLong("scratch-path"), help: "Specify a custom scratch directory path (default .build)", completion: .directory) @@ -66,7 +62,7 @@ struct SwiftBootstrapBuildTool: ParsableCommand { } @Option(name: .shortAndLong, help: "Build with configuration") - public var configuration: BuildConfiguration = .debug + package var configuration: BuildConfiguration = .debug @Option(name: .customLong("Xcc", withSingleDash: true), parsing: .unconditionalSingleValue, @@ -93,36 +89,36 @@ struct SwiftBootstrapBuildTool: ParsableCommand { help: ArgumentHelp( "Pass flag through to the Xcode build system invocations", visibility: .hidden)) - public var xcbuildFlags: [String] = [] + package var xcbuildFlags: [String] = [] @Option(name: .customLong("Xbuild-tools-swiftc", withSingleDash: true), parsing: .unconditionalSingleValue, help: ArgumentHelp("Pass flag to the manifest build invocation", visibility: .hidden)) - public var manifestFlags: [String] = [] + package var manifestFlags: [String] = [] @Option( name: .customLong("arch"), help: ArgumentHelp("Build the package for the these architectures", visibility: .hidden)) - public var architectures: [String] = [] + package var architectures: [String] = [] /// The verbosity of informational output. @Flag(name: .shortAndLong, help: "Increase verbosity to include informational output") - public var verbose: Bool = false + package var verbose: Bool = false /// The verbosity of informational output. @Flag(name: [.long, .customLong("vv")], help: "Increase verbosity to include debug output") - public var veryVerbose: Bool = false + package var veryVerbose: Bool = false /// Whether to use the integrated Swift driver rather than shelling out /// to a separate process. @Flag() - public var useIntegratedSwiftDriver: Bool = false + package var useIntegratedSwiftDriver: Bool = false /// A flag that indicates this build should check whether targets only import /// their explicitly-declared dependencies @Option() - public var explicitTargetDependencyImportCheck: TargetDependencyImportCheckingMode = .none + package var explicitTargetDependencyImportCheck: TargetDependencyImportCheckingMode = .none enum TargetDependencyImportCheckingMode: String, Codable, ExpressibleByArgument { case none @@ -131,7 +127,7 @@ struct SwiftBootstrapBuildTool: ParsableCommand { /// Disables adding $ORIGIN/@loader_path to the rpath, useful when deploying @Flag(name: .customLong("disable-local-rpath"), help: "Disable adding $ORIGIN/@loader_path to the rpath by default") - public var shouldDisableLocalRpath: Bool = false + package var shouldDisableLocalRpath: Bool = false private var buildSystem: BuildSystemProvider.Kind { #if os(macOS) @@ -143,7 +139,7 @@ struct SwiftBootstrapBuildTool: ParsableCommand { #endif } - public var buildFlags: BuildFlags { + package var buildFlags: BuildFlags { BuildFlags( cCompilerFlags: self.cCompilerFlags, cxxCompilerFlags: self.cxxCompilerFlags, @@ -163,9 +159,9 @@ struct SwiftBootstrapBuildTool: ParsableCommand { } } - public init() {} + package init() {} - public func run() throws { + package func run() throws { do { let fileSystem = localFileSystem diff --git a/Sources/swift-build/Entrypoint.swift b/Sources/swift-build/Entrypoint.swift index 12dfee5167b..0dcb53302ec 100644 --- a/Sources/swift-build/Entrypoint.swift +++ b/Sources/swift-build/Entrypoint.swift @@ -10,7 +10,6 @@ // //===----------------------------------------------------------------------===// -@_spi(SwiftPMInternal) import Commands @main diff --git a/Sources/swift-package-manager/SwiftPM.swift b/Sources/swift-package-manager/SwiftPM.swift index 3b0523ad728..e16fe89dbd3 100644 --- a/Sources/swift-package-manager/SwiftPM.swift +++ b/Sources/swift-package-manager/SwiftPM.swift @@ -12,7 +12,6 @@ import Basics -@_spi(SwiftPMInternal) import Commands import SwiftSDKCommand diff --git a/Sources/swift-run/Entrypoint.swift b/Sources/swift-run/Entrypoint.swift index 8ddc1ea3da7..4976cccdb15 100644 --- a/Sources/swift-run/Entrypoint.swift +++ b/Sources/swift-run/Entrypoint.swift @@ -10,7 +10,6 @@ // //===----------------------------------------------------------------------===// -@_spi(SwiftPMInternal) import Commands @main diff --git a/Sources/swift-test/main.swift b/Sources/swift-test/main.swift index a74668facc3..01cef92cb73 100644 --- a/Sources/swift-test/main.swift +++ b/Sources/swift-test/main.swift @@ -10,7 +10,6 @@ // //===----------------------------------------------------------------------===// -@_spi(SwiftPMInternal) import Commands SwiftTestCommand.main() diff --git a/Tests/BasicsTests/ProgressAnimationTests.swift b/Tests/BasicsTests/ProgressAnimationTests.swift index b93ef46c2a9..eca14b4e9c8 100644 --- a/Tests/BasicsTests/ProgressAnimationTests.swift +++ b/Tests/BasicsTests/ProgressAnimationTests.swift @@ -13,7 +13,6 @@ import _Concurrency import XCTest -@_spi(SwiftPMInternal) @testable import Basics diff --git a/Tests/BuildTests/BuildOperationTests.swift b/Tests/BuildTests/BuildOperationTests.swift index 94061628aee..8574f12f198 100644 --- a/Tests/BuildTests/BuildOperationTests.swift +++ b/Tests/BuildTests/BuildOperationTests.swift @@ -10,7 +10,6 @@ // //===----------------------------------------------------------------------===// -@_spi(SwiftPMInternal) @testable import Build @@ -20,7 +19,6 @@ import PackageModel import Basics import SPMTestSupport -@_spi(SwiftPMInternal) import SPMBuildCore import XCTest diff --git a/Tests/BuildTests/BuildPlanTests.swift b/Tests/BuildTests/BuildPlanTests.swift index e461ac1e93d..a4c79b412df 100644 --- a/Tests/BuildTests/BuildPlanTests.swift +++ b/Tests/BuildTests/BuildPlanTests.swift @@ -14,7 +14,6 @@ @testable import Build @testable -@_spi(SwiftPMInternal) import DriverSupport @testable import PackageGraph diff --git a/Tests/BuildTests/CrossCompilationBuildPlanTests.swift b/Tests/BuildTests/CrossCompilationBuildPlanTests.swift index dcb996bed3c..028ea94f3cd 100644 --- a/Tests/BuildTests/CrossCompilationBuildPlanTests.swift +++ b/Tests/BuildTests/CrossCompilationBuildPlanTests.swift @@ -20,15 +20,12 @@ import class PackageModel.Manifest import struct PackageModel.TargetDescription import func SPMTestSupport.loadPackageGraph -@_spi(SwiftPMInternal) import func SPMTestSupport.embeddedCxxInteropPackageGraph -@_spi(SwiftPMInternal) import func SPMTestSupport.macrosPackageGraph import func SPMTestSupport.mockBuildParameters -@_spi(SwiftPMInternal) import func SPMTestSupport.trivialPackageGraph import struct SPMTestSupport.BuildPlanResult diff --git a/Tests/BuildTests/LLBuildManifestBuilderTests.swift b/Tests/BuildTests/LLBuildManifestBuilderTests.swift index f66e0dc5e25..c9119b0a919 100644 --- a/Tests/BuildTests/LLBuildManifestBuilderTests.swift +++ b/Tests/BuildTests/LLBuildManifestBuilderTests.swift @@ -17,7 +17,6 @@ import PackageGraph import PackageModel import struct SPMBuildCore.BuildParameters -@_spi(SwiftPMInternal) import SPMTestSupport import class TSCBasic.InMemoryFileSystem diff --git a/Tests/CommandsTests/APIDiffTests.swift b/Tests/CommandsTests/APIDiffTests.swift index 0f700957ac8..fe934bcb4e9 100644 --- a/Tests/CommandsTests/APIDiffTests.swift +++ b/Tests/CommandsTests/APIDiffTests.swift @@ -14,7 +14,6 @@ import Basics import Build import Commands -@_spi(SwiftPMInternal) import DriverSupport import Foundation diff --git a/Tests/CommandsTests/BuildCommandTests.swift b/Tests/CommandsTests/BuildCommandTests.swift index 9af7b42a3a0..b54c6726dbb 100644 --- a/Tests/CommandsTests/BuildCommandTests.swift +++ b/Tests/CommandsTests/BuildCommandTests.swift @@ -13,7 +13,6 @@ import Basics @testable import Commands -@_spi(SwiftPMInternal) @testable import CoreCommands diff --git a/Tests/CommandsTests/PackageCommandTests.swift b/Tests/CommandsTests/PackageCommandTests.swift index bc5ed0ccaf9..73e2d58db2d 100644 --- a/Tests/CommandsTests/PackageCommandTests.swift +++ b/Tests/CommandsTests/PackageCommandTests.swift @@ -12,7 +12,6 @@ import Basics -@_spi(SwiftPMInternal) @testable import CoreCommands diff --git a/Tests/CommandsTests/SwiftCommandStateTests.swift b/Tests/CommandsTests/SwiftCommandStateTests.swift index 331cbfe1cc5..b1d0b8fdfcc 100644 --- a/Tests/CommandsTests/SwiftCommandStateTests.swift +++ b/Tests/CommandsTests/SwiftCommandStateTests.swift @@ -13,7 +13,6 @@ @testable import Basics @testable import Build -@_spi(SwiftPMInternal) @testable import CoreCommands diff --git a/Tests/PackageModelTests/SwiftSDKBundleTests.swift b/Tests/PackageModelTests/SwiftSDKBundleTests.swift index 5650d09ec33..0491ba1e344 100644 --- a/Tests/PackageModelTests/SwiftSDKBundleTests.swift +++ b/Tests/PackageModelTests/SwiftSDKBundleTests.swift @@ -11,7 +11,6 @@ //===----------------------------------------------------------------------===// import Basics -@_spi(SwiftPMInternal) @testable import PackageModel import SPMTestSupport import XCTest From 68b62365d19547c916d76b82503f5ffd72c37bb7 Mon Sep 17 00:00:00 2001 From: Rauhul Varma Date: Thu, 29 Feb 2024 14:57:36 -0800 Subject: [PATCH 038/159] Make ignore-lock a flag (#7384) This allows users to ignore package locking via `--ignore-lock` instead of `--ignore-lock true`. --- Sources/CoreCommands/Options.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/CoreCommands/Options.swift b/Sources/CoreCommands/Options.swift index f7070eba34f..bcb60853c23 100644 --- a/Sources/CoreCommands/Options.swift +++ b/Sources/CoreCommands/Options.swift @@ -126,7 +126,7 @@ package struct LocationOptions: ParsableArguments { ) package var pkgConfigDirectories: [AbsolutePath] = [] - @Option(name: .customLong("ignore-lock"), help: .hidden) + @Flag(name: .customLong("ignore-lock"), help: .hidden) package var ignoreLock: Bool = false } From 1d0be84d3f52bf956335422dd80a09021a63b18d Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 1 Mar 2024 17:05:02 +0000 Subject: [PATCH 039/159] Remove uses of `temp_await` from `SwiftTestCommand` (#7382) This prevents possible deadlocks that can be caused by blocking Swift Concurrency threads with semaphores. --- Sources/Commands/SwiftTestCommand.swift | 35 +++++++++---------- Sources/SPMTestSupport/XCTAssertHelpers.swift | 10 ++++++ Sources/swift-package-manager/SwiftPM.swift | 2 +- Sources/swift-test/CMakeLists.txt | 5 ++- .../{main.swift => Entrypoint.swift} | 7 +++- Tests/BuildTests/PluginsBuildPlanTests.swift | 2 +- .../PackageGraphPerfTests.swift | 2 +- 7 files changed, 39 insertions(+), 24 deletions(-) rename Sources/swift-test/{main.swift => Entrypoint.swift} (83%) diff --git a/Sources/Commands/SwiftTestCommand.swift b/Sources/Commands/SwiftTestCommand.swift index 0297a4c36a1..58b39b3d1fa 100644 --- a/Sources/Commands/SwiftTestCommand.swift +++ b/Sources/Commands/SwiftTestCommand.swift @@ -179,7 +179,7 @@ package enum TestOutput: String, ExpressibleByArgument { } /// swift-test tool namespace -package struct SwiftTestCommand: SwiftCommand { +package struct SwiftTestCommand: AsyncSwiftCommand { package static var configuration = CommandConfiguration( commandName: "test", _superCommandName: "swift", @@ -200,7 +200,7 @@ package struct SwiftTestCommand: SwiftCommand { // MARK: - XCTest - private func xctestRun(_ swiftCommandState: SwiftCommandState) throws { + private func xctestRun(_ swiftCommandState: SwiftCommandState) async throws { // validate XCTest available on darwin based systems let toolchain = try swiftCommandState.getTargetToolchain() let isHostTestingAvailable = try swiftCommandState.getHostToolchain().swiftSDK.supportsTesting @@ -218,7 +218,7 @@ package struct SwiftTestCommand: SwiftCommand { let testProducts = try buildTestsIfNeeded(swiftCommandState: swiftCommandState, library: .xctest) if !self.options.shouldRunInParallel { let xctestArgs = try xctestArgs(for: testProducts, swiftCommandState: swiftCommandState) - try runTestProducts( + try await runTestProducts( testProducts, additionalArguments: xctestArgs, buildParameters: buildParameters, @@ -269,7 +269,7 @@ package struct SwiftTestCommand: SwiftCommand { // process code Coverage if request if self.options.enableCodeCoverage, runner.ranSuccessfully { - try processCodeCoverage(testProducts, swiftCommandState: swiftCommandState, library: .xctest) + try await processCodeCoverage(testProducts, swiftCommandState: swiftCommandState, library: .xctest) } if !runner.ranSuccessfully { @@ -337,11 +337,11 @@ package struct SwiftTestCommand: SwiftCommand { // MARK: - swift-testing - private func swiftTestingRun(_ swiftCommandState: SwiftCommandState) throws { + private func swiftTestingRun(_ swiftCommandState: SwiftCommandState) async throws { let buildParameters = try swiftCommandState.buildParametersForTest(options: self.options, library: .swiftTesting) let testProducts = try buildTestsIfNeeded(swiftCommandState: swiftCommandState, library: .swiftTesting) let additionalArguments = Array(CommandLine.arguments.dropFirst()) - try runTestProducts( + try await runTestProducts( testProducts, additionalArguments: additionalArguments, buildParameters: buildParameters, @@ -352,7 +352,7 @@ package struct SwiftTestCommand: SwiftCommand { // MARK: - Common implementation - package func run(_ swiftCommandState: SwiftCommandState) throws { + package func run(_ swiftCommandState: SwiftCommandState) async throws { do { // Validate commands arguments try self.validateArguments(observabilityScope: swiftCommandState.observabilityScope) @@ -369,10 +369,10 @@ package struct SwiftTestCommand: SwiftCommand { try command.run(swiftCommandState) } else { if try options.testLibraryOptions.enableSwiftTestingLibrarySupport(swiftCommandState: swiftCommandState) { - try swiftTestingRun(swiftCommandState) + try await swiftTestingRun(swiftCommandState) } if options.testLibraryOptions.enableXCTestSupport { - try xctestRun(swiftCommandState) + try await xctestRun(swiftCommandState) } } } @@ -383,7 +383,7 @@ package struct SwiftTestCommand: SwiftCommand { buildParameters: BuildParameters, swiftCommandState: SwiftCommandState, library: BuildParameters.Testing.Library - ) throws { + ) async throws { // Clean out the code coverage directory that may contain stale // profraw files from a previous run of the code coverage tool. if self.options.enableCodeCoverage { @@ -418,7 +418,7 @@ package struct SwiftTestCommand: SwiftCommand { } if self.options.enableCodeCoverage, ranSuccessfully { - try processCodeCoverage(testProducts, swiftCommandState: swiftCommandState, library: library) + try await processCodeCoverage(testProducts, swiftCommandState: swiftCommandState, library: library) } if self.options.enableExperimentalTestOutput, !ranSuccessfully { @@ -462,16 +462,13 @@ package struct SwiftTestCommand: SwiftCommand { _ testProducts: [BuiltTestProduct], swiftCommandState: SwiftCommandState, library: BuildParameters.Testing.Library - ) throws { + ) async throws { let workspace = try swiftCommandState.getActiveWorkspace() let root = try swiftCommandState.getWorkspaceRoot() - let rootManifests = try temp_await { - workspace.loadRootManifests( - packages: root.packages, - observabilityScope: swiftCommandState.observabilityScope, - completion: $0 - ) - } + let rootManifests = try await workspace.loadRootManifests( + packages: root.packages, + observabilityScope: swiftCommandState.observabilityScope + ) guard let rootManifest = rootManifests.values.first else { throw StringError("invalid manifests at \(root.packages)") } diff --git a/Sources/SPMTestSupport/XCTAssertHelpers.swift b/Sources/SPMTestSupport/XCTAssertHelpers.swift index 67adb785fcb..e63d65a2b91 100644 --- a/Sources/SPMTestSupport/XCTAssertHelpers.swift +++ b/Sources/SPMTestSupport/XCTAssertHelpers.swift @@ -170,6 +170,16 @@ package func XCTAssertAsyncFalse( XCTAssertFalse(result, message(), file: file, line: line) } +package func XCTAssertAsyncNil( + _ expression: @autoclosure () async throws -> Any?, + _ message: @autoclosure () -> String = "", + file: StaticString = #filePath, + line: UInt = #line +) async rethrows { + let result = try await expression() + XCTAssertNil(result, message(), file: file, line: line) +} + package func XCTAssertThrowsCommandExecutionError( _ expression: @autoclosure () throws -> T, _ message: @autoclosure () -> String = "", diff --git a/Sources/swift-package-manager/SwiftPM.swift b/Sources/swift-package-manager/SwiftPM.swift index e16fe89dbd3..84ea0f28baa 100644 --- a/Sources/swift-package-manager/SwiftPM.swift +++ b/Sources/swift-package-manager/SwiftPM.swift @@ -44,7 +44,7 @@ struct SwiftPM { case "swift-experimental-sdk": await SwiftSDKCommand.main() case "swift-test": - SwiftTestCommand.main() + await SwiftTestCommand.main() case "swift-run": await SwiftRunCommand.main() case "swift-package-collection": diff --git a/Sources/swift-test/CMakeLists.txt b/Sources/swift-test/CMakeLists.txt index 896da188ad9..79c910294fd 100644 --- a/Sources/swift-test/CMakeLists.txt +++ b/Sources/swift-test/CMakeLists.txt @@ -7,9 +7,12 @@ # See http://swift.org/CONTRIBUTORS.txt for Swift project authors add_executable(swift-test - main.swift) + Entrypoint.swift) target_link_libraries(swift-test PRIVATE Commands) +target_compile_options(swift-test PRIVATE + -parse-as-library) + install(TARGETS swift-test RUNTIME DESTINATION bin) diff --git a/Sources/swift-test/main.swift b/Sources/swift-test/Entrypoint.swift similarity index 83% rename from Sources/swift-test/main.swift rename to Sources/swift-test/Entrypoint.swift index 01cef92cb73..eb1c4d1b37a 100644 --- a/Sources/swift-test/main.swift +++ b/Sources/swift-test/Entrypoint.swift @@ -12,4 +12,9 @@ import Commands -SwiftTestCommand.main() +@main +struct Entrypoint { + static func main() async { + await SwiftTestCommand.main() + } +} diff --git a/Tests/BuildTests/PluginsBuildPlanTests.swift b/Tests/BuildTests/PluginsBuildPlanTests.swift index 5ef99e3fc59..892c3da6725 100644 --- a/Tests/BuildTests/PluginsBuildPlanTests.swift +++ b/Tests/BuildTests/PluginsBuildPlanTests.swift @@ -19,7 +19,7 @@ import PackageModel final class PluginsBuildPlanTests: XCTestCase { func testBuildToolsDatabasePath() throws { try fixture(name: "Miscellaneous/Plugins/MySourceGenPlugin") { fixturePath in - let (stdout, stderr) = try executeSwiftBuild(fixturePath) + let (stdout, _) = try executeSwiftBuild(fixturePath) XCTAssertMatch(stdout, .contains("Build complete!")) XCTAssertTrue(localFileSystem.exists(fixturePath.appending(RelativePath(".build/plugins/tools/build.db")))) } diff --git a/Tests/PackageGraphPerformanceTests/PackageGraphPerfTests.swift b/Tests/PackageGraphPerformanceTests/PackageGraphPerfTests.swift index 622eeb68a83..c688b1d5439 100644 --- a/Tests/PackageGraphPerformanceTests/PackageGraphPerfTests.swift +++ b/Tests/PackageGraphPerformanceTests/PackageGraphPerfTests.swift @@ -151,7 +151,7 @@ final class PackageGraphPerfTests: XCTestCasePerf { measure { do { for _ in 0.. Date: Mon, 4 Mar 2024 00:27:51 +0100 Subject: [PATCH 040/159] Add XDG Base Directory Specification support (#7386) ### Motivation: As discussed in #6204, users would like to have a clean home directory. And in the discussions following the merge of #3430, it seems there is a willingness to adhere to the XDG spec, and support `~/.config/swiftpm`. ### Modifications: If the `XDG_CONFIG_HOME` environmental variable is defined, use `$XDG_CONFIG_HOME/swiftpm` as the root `dotSwiftPM` directory. ### Result: The symlinks that were previously stored in `~/.swiftpm` are now stored in `$XDG_CONFIG_HOME/swiftpm` when this variable is defined, therefore not cluttering the home directory of users. Closes #6204 --------- Co-authored-by: Max Desiatov --- Sources/Basics/FileSystem/FileSystem+Extensions.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Sources/Basics/FileSystem/FileSystem+Extensions.swift b/Sources/Basics/FileSystem/FileSystem+Extensions.swift index 9b62e94ae79..ac0c0ace718 100644 --- a/Sources/Basics/FileSystem/FileSystem+Extensions.swift +++ b/Sources/Basics/FileSystem/FileSystem+Extensions.swift @@ -211,9 +211,14 @@ extension FileSystem { extension FileSystem { /// SwiftPM directory under user's home directory (~/.swiftpm) + /// or under $XDG_CONFIG_HOME/swiftpm if the environmental variable is defined public var dotSwiftPM: AbsolutePath { get throws { - try self.homeDirectory.appending(".swiftpm") + if let configurationDirectory = EnvironmentVariables.process()["XDG_CONFIG_HOME"] { + return try AbsolutePath(validating: configurationDirectory).appending("swiftpm") + } else { + return try self.homeDirectory.appending(".swiftpm") + } } } From 5185c0395aefd327e5ed669f4a88427fc8123f20 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Mon, 4 Mar 2024 16:54:24 +0000 Subject: [PATCH 041/159] Hide more llbuild-specific APIs (#7387) These APIs should not be marked `public` to prevent SwiftPM clients from adopting them by accident. --- .../ClangTargetBuildDescription.swift | 28 +-- .../BuildDescription/PluginDescription.swift | 12 +- .../ProductBuildDescription.swift | 18 +- .../SwiftTargetBuildDescription.swift | 36 ++-- .../TargetBuildDescription.swift | 4 +- .../LLBuildManifestBuilder+Swift.swift | 2 +- .../LLBuildManifestBuilder.swift | 28 +-- Sources/Build/BuildOperation.swift | 30 ++-- ...dOperationBuildSystemDelegateHandler.swift | 26 +-- Sources/Build/BuildPlan/BuildPlan.swift | 22 +-- Sources/Build/ClangSupport.swift | 4 +- Sources/Build/SwiftCompilerOutputParser.swift | 70 ++++---- Sources/Build/TestObservation.swift | 2 +- Sources/LLBuildManifest/Command.swift | 6 +- Sources/LLBuildManifest/LLBuildManifest.swift | 94 +++++----- .../LLBuildManifestWriter.swift | 24 +-- Sources/LLBuildManifest/Node.swift | 26 +-- Sources/LLBuildManifest/Target.swift | 8 +- Sources/LLBuildManifest/Tools.swift | 170 +++++++++--------- 19 files changed, 305 insertions(+), 305 deletions(-) diff --git a/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift b/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift index 90d37912ab8..a95ab57a944 100644 --- a/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift @@ -22,17 +22,17 @@ import struct SPMBuildCore.PrebuildCommandResult import enum TSCBasic.ProcessEnv /// Target description for a Clang target i.e. C language family target. -public final class ClangTargetBuildDescription { +package final class ClangTargetBuildDescription { /// The target described by this target. - public let target: ResolvedTarget + package let target: ResolvedTarget /// The underlying clang target. - public let clangTarget: ClangTarget + package let clangTarget: ClangTarget /// The tools version of the package that declared the target. This can /// can be used to conditionalize semantically significant changes in how /// a target is built. - public let toolsVersion: ToolsVersion + package let toolsVersion: ToolsVersion /// The build parameters. let buildParameters: BuildParameters @@ -43,7 +43,7 @@ public final class ClangTargetBuildDescription { } /// The list of all resource files in the target, including the derived ones. - public var resources: [Resource] { + package var resources: [Resource] { self.target.underlying.resources + self.pluginDerivedResources } @@ -61,7 +61,7 @@ public final class ClangTargetBuildDescription { } /// The modulemap file for this target, if any. - public private(set) var moduleMap: AbsolutePath? + package private(set) var moduleMap: AbsolutePath? /// Path to the temporary directory for this target. var tempsPath: AbsolutePath @@ -78,13 +78,13 @@ public final class ClangTargetBuildDescription { private var pluginDerivedResources: [Resource] /// Path to the resource accessor header file, if generated. - public private(set) var resourceAccessorHeaderFile: AbsolutePath? + package private(set) var resourceAccessorHeaderFile: AbsolutePath? /// Path to the resource Info.plist file, if generated. - public private(set) var resourceBundleInfoPlistPath: AbsolutePath? + package private(set) var resourceBundleInfoPlistPath: AbsolutePath? /// The objects in this target. - public var objects: [AbsolutePath] { + package var objects: [AbsolutePath] { get throws { try compilePaths().map(\.object) } @@ -100,12 +100,12 @@ public final class ClangTargetBuildDescription { private let fileSystem: FileSystem /// If this target is a test target. - public var isTestTarget: Bool { + package var isTestTarget: Bool { target.type == .test } /// The results of applying any build tool plugins to this target. - public let buildToolPluginInvocationResults: [BuildToolPluginInvocationResult] + package let buildToolPluginInvocationResults: [BuildToolPluginInvocationResult] /// Create a new target description with target and build parameters. init( @@ -182,7 +182,7 @@ public final class ClangTargetBuildDescription { } /// An array of tuples containing filename, source, object and dependency path for each of the source in this target. - public func compilePaths() + package func compilePaths() throws -> [(filename: RelativePath, source: AbsolutePath, object: AbsolutePath, deps: AbsolutePath)] { let sources = [ @@ -206,7 +206,7 @@ public final class ClangTargetBuildDescription { /// NOTE: The parameter to specify whether to get C++ semantics is currently optional, but this is only for revlock /// avoidance with clients. Callers should always specify what they want based either the user's indication or on a /// default value (possibly based on the filename suffix). - public func basicArguments( + package func basicArguments( isCXX isCXXOverride: Bool? = .none, isC: Bool = false ) throws -> [String] { @@ -308,7 +308,7 @@ public final class ClangTargetBuildDescription { return args } - public func emitCommandLine(for filePath: AbsolutePath) throws -> [String] { + package func emitCommandLine(for filePath: AbsolutePath) throws -> [String] { let standards = [ (clangTarget.cxxLanguageStandard, SupportedLanguageExtension.cppExtensions), (clangTarget.cLanguageStandard, SupportedLanguageExtension.cExtensions), diff --git a/Sources/Build/BuildDescription/PluginDescription.swift b/Sources/Build/BuildDescription/PluginDescription.swift index 03a8d62dc0b..72614a96239 100644 --- a/Sources/Build/BuildDescription/PluginDescription.swift +++ b/Sources/Build/BuildDescription/PluginDescription.swift @@ -21,24 +21,24 @@ import protocol Basics.FileSystem /// But because the package graph and build plan are not loaded for incremental /// builds, this information is included in the BuildDescription, and the plugin /// targets are compiled directly. -public final class PluginDescription: Codable { +package final class PluginDescription: Codable { /// The identity of the package in which the plugin is defined. - public let package: PackageIdentity + package let package: PackageIdentity /// The name of the plugin target in that package (this is also the name of /// the plugin). - public let targetName: String + package let targetName: String /// The names of any plugin products in that package that vend the plugin /// to other packages. - public let productNames: [String] + package let productNames: [String] /// The tools version of the package that declared the target. This affects /// the API that is available in the PackagePlugin module. - public let toolsVersion: ToolsVersion + package let toolsVersion: ToolsVersion /// Swift source files that comprise the plugin. - public let sources: Sources + package let sources: Sources /// Initialize a new plugin target description. The target is expected to be /// a `PluginTarget`. diff --git a/Sources/Build/BuildDescription/ProductBuildDescription.swift b/Sources/Build/BuildDescription/ProductBuildDescription.swift index dd0bd9024db..2b662984c8a 100644 --- a/Sources/Build/BuildDescription/ProductBuildDescription.swift +++ b/Sources/Build/BuildDescription/ProductBuildDescription.swift @@ -21,25 +21,25 @@ import SPMBuildCore import struct TSCBasic.SortedArray /// The build description for a product. -public final class ProductBuildDescription: SPMBuildCore.ProductBuildDescription { +package final class ProductBuildDescription: SPMBuildCore.ProductBuildDescription { /// The reference to the product. - public let package: ResolvedPackage + package let package: ResolvedPackage /// The reference to the product. - public let product: ResolvedProduct + package let product: ResolvedProduct /// The tools version of the package that declared the product. This can /// can be used to conditionalize semantically significant changes in how /// a target is built. - public let toolsVersion: ToolsVersion + package let toolsVersion: ToolsVersion /// The build parameters. - public let buildParameters: BuildParameters + package let buildParameters: BuildParameters /// All object files to link into this product. /// // Computed during build planning. - public internal(set) var objects = SortedArray() + package internal(set) var objects = SortedArray() /// The dynamic libraries this product needs to link with. // Computed during build planning. @@ -128,7 +128,7 @@ public final class ProductBuildDescription: SPMBuildCore.ProductBuildDescription } /// The arguments to the librarian to create a static library. - public func archiveArguments() throws -> [String] { + package func archiveArguments() throws -> [String] { let librarian = self.buildParameters.toolchain.librarianPath.pathString let triple = self.buildParameters.triple if triple.isWindows(), librarian.hasSuffix("link") || librarian.hasSuffix("link.exe") { @@ -141,7 +141,7 @@ public final class ProductBuildDescription: SPMBuildCore.ProductBuildDescription } /// The arguments to link and create this product. - public func linkArguments() throws -> [String] { + package func linkArguments() throws -> [String] { var args = [buildParameters.toolchain.swiftCompilerPath.pathString] args += self.buildParameters.sanitizers.linkSwiftFlags() args += self.additionalFlags @@ -390,7 +390,7 @@ public final class ProductBuildDescription: SPMBuildCore.ProductBuildDescription } extension SortedArray where Element == AbsolutePath { - public static func +=(lhs: inout SortedArray, rhs: S) where S.Iterator.Element == AbsolutePath { + package static func +=(lhs: inout SortedArray, rhs: S) where S.Iterator.Element == AbsolutePath { lhs.insert(contentsOf: rhs) } } diff --git a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift index f65c1439d41..b59b328f3ca 100644 --- a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift @@ -28,19 +28,19 @@ import DriverSupport import struct TSCBasic.ByteString /// Target description for a Swift target. -public final class SwiftTargetBuildDescription { +package final class SwiftTargetBuildDescription { /// The package this target belongs to. - public let package: ResolvedPackage + package let package: ResolvedPackage /// The target described by this target. - public let target: ResolvedTarget + package let target: ResolvedTarget private let swiftTarget: SwiftTarget /// The tools version of the package that declared the target. This can /// can be used to conditionalize semantically significant changes in how /// a target is built. - public let toolsVersion: ToolsVersion + package let toolsVersion: ToolsVersion /// The build parameters. let buildParameters: BuildParameters @@ -77,22 +77,22 @@ public final class SwiftTargetBuildDescription { } /// The list of all source files in the target, including the derived ones. - public var sources: [AbsolutePath] { + package var sources: [AbsolutePath] { self.target.sources.paths + self.derivedSources.paths + self.pluginDerivedSources.paths } - public var sourcesFileListPath: AbsolutePath { + package var sourcesFileListPath: AbsolutePath { self.tempsPath.appending(component: "sources") } /// The list of all resource files in the target, including the derived ones. - public var resources: [Resource] { + package var resources: [Resource] { self.target.underlying.resources + self.pluginDerivedResources } /// The objects in this target, containing either machine code or bitcode /// depending on the build parameters used. - public var objects: [AbsolutePath] { + package var objects: [AbsolutePath] { get throws { let relativeSources = self.target.sources.relativePaths + self.derivedSources.relativePaths @@ -112,7 +112,7 @@ public final class SwiftTargetBuildDescription { } /// The path to the swiftmodule file after compilation. - public var moduleOutputPath: AbsolutePath { // note: needs to be public because of sourcekit-lsp + public var moduleOutputPath: AbsolutePath { // note: needs to be `public` because of sourcekit-lsp // If we're an executable and we're not allowing test targets to link against us, we hide the module. let triple = buildParameters.triple let allowLinkingAgainstExecutables = (triple.isDarwin() || triple.isLinux() || triple.isWindows()) && self.toolsVersion >= .v5_5 @@ -133,7 +133,7 @@ public final class SwiftTargetBuildDescription { } /// Path to the resource Info.plist file, if generated. - public private(set) var resourceBundleInfoPlistPath: AbsolutePath? + package private(set) var resourceBundleInfoPlistPath: AbsolutePath? /// Paths to the binary libraries the target depends on. var libraryBinaryPaths: Set = [] @@ -148,7 +148,7 @@ public final class SwiftTargetBuildDescription { /// Describes the purpose of a test target, including any special roles such as containing a list of discovered /// tests or serving as the manifest target which contains the main entry point. - public enum TestTargetRole { + package enum TestTargetRole { /// An ordinary test target, defined explicitly in a package, containing test code. case `default` @@ -163,10 +163,10 @@ public final class SwiftTargetBuildDescription { case entryPoint(isSynthesized: Bool) } - public let testTargetRole: TestTargetRole? + package let testTargetRole: TestTargetRole? /// If this target is a test target. - public var isTestTarget: Bool { + package var isTestTarget: Bool { self.testTargetRole != nil } @@ -228,13 +228,13 @@ public final class SwiftTargetBuildDescription { private(set) var moduleMap: AbsolutePath? /// The results of applying any build tool plugins to this target. - public let buildToolPluginInvocationResults: [BuildToolPluginInvocationResult] + package let buildToolPluginInvocationResults: [BuildToolPluginInvocationResult] /// The results of running any prebuild commands for this target. - public let prebuildCommandResults: [PrebuildCommandResult] + package let prebuildCommandResults: [PrebuildCommandResult] /// Any macro products that this target requires to build. - public let requiredMacroProducts: [ResolvedProduct] + package let requiredMacroProducts: [ResolvedProduct] /// ObservabilityScope with which to emit diagnostics private let observabilityScope: ObservabilityScope @@ -474,7 +474,7 @@ public final class SwiftTargetBuildDescription { } /// The arguments needed to compile this target. - public func compileArguments() throws -> [String] { + package func compileArguments() throws -> [String] { var args = [String]() args += try self.buildParameters.targetTripleArgs(for: self.target) args += ["-swift-version", self.swiftVersion.rawValue] @@ -650,7 +650,7 @@ public final class SwiftTargetBuildDescription { /// When `scanInvocation` argument is set to `true`, omit the side-effect producing arguments /// such as emitting a module or supplementary outputs. - public func emitCommandLine(scanInvocation: Bool = false) throws -> [String] { + package func emitCommandLine(scanInvocation: Bool = false) throws -> [String] { var result: [String] = [] result.append(self.buildParameters.toolchain.swiftCompilerPath.pathString) diff --git a/Sources/Build/BuildDescription/TargetBuildDescription.swift b/Sources/Build/BuildDescription/TargetBuildDescription.swift index 4fae9198680..04462cf57b4 100644 --- a/Sources/Build/BuildDescription/TargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/TargetBuildDescription.swift @@ -17,12 +17,12 @@ import struct PackageModel.ToolsVersion import struct SPMBuildCore.BuildToolPluginInvocationResult import struct SPMBuildCore.BuildParameters -public enum BuildDescriptionError: Swift.Error { +package enum BuildDescriptionError: Swift.Error { case requestedFileNotPartOfTarget(targetName: String, requestedFilePath: AbsolutePath) } /// A target description which can either be for a Swift or Clang target. -public enum TargetBuildDescription { +package enum TargetBuildDescription { /// Swift target description. case swift(SwiftTargetBuildDescription) diff --git a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift index a51bf24d954..144623b3f1d 100644 --- a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift +++ b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift @@ -188,7 +188,7 @@ extension LLBuildManifestBuilder { // dependency graph of B. The driver is then responsible for the necessary post-processing // to merge the dependency graphs and plan the build for A, using artifacts of B as explicit // inputs. - public func addTargetsToExplicitBuildManifest() throws { + package func addTargetsToExplicitBuildManifest() throws { // Sort the product targets in topological order in order to collect and "bubble up" // their respective dependency graphs to the depending targets. let nodes: [ResolvedTarget.Dependency] = try self.plan.targetMap.keys.compactMap { diff --git a/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift b/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift index 8ff1455360a..73966ce6bf9 100644 --- a/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift +++ b/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift @@ -27,7 +27,7 @@ import enum TSCBasic.ProcessEnv import func TSCBasic.topologicalSort /// High-level interface to ``LLBuildManifest`` and ``LLBuildManifestWriter``. -public class LLBuildManifestBuilder { +package class LLBuildManifestBuilder { enum Error: Swift.Error { case ldPathDriverOptionUnavailable(option: String) @@ -39,11 +39,11 @@ public class LLBuildManifestBuilder { } } - public enum TargetKind { + package enum TargetKind { case main case test - public var targetName: String { + package var targetName: String { switch self { case .main: return "main" case .test: return "test" @@ -52,24 +52,24 @@ public class LLBuildManifestBuilder { } /// The build plan to work on. - public let plan: BuildPlan + package let plan: BuildPlan /// Whether to sandbox commands from build tool plugins. - public let disableSandboxForPluginCommands: Bool + package let disableSandboxForPluginCommands: Bool /// File system reference. let fileSystem: any FileSystem /// ObservabilityScope with which to emit diagnostics - public let observabilityScope: ObservabilityScope + package let observabilityScope: ObservabilityScope - public internal(set) var manifest: LLBuildManifest = .init() + package internal(set) var manifest: LLBuildManifest = .init() /// Mapping from Swift compiler path to Swift get version files. var swiftGetVersionFiles = [AbsolutePath: AbsolutePath]() /// Create a new builder with a build plan. - public init( + package init( _ plan: BuildPlan, disableSandboxForPluginCommands: Bool = false, fileSystem: any FileSystem, @@ -85,7 +85,7 @@ public class LLBuildManifestBuilder { /// Generate build manifest at the given path. @discardableResult - public func generateManifest(at path: AbsolutePath) throws -> LLBuildManifest { + package func generateManifest(at path: AbsolutePath) throws -> LLBuildManifest { self.swiftGetVersionFiles.removeAll() self.manifest.createTarget(TargetKind.main.targetName) @@ -317,21 +317,21 @@ extension TargetBuildDescription { } extension ResolvedTarget { - public func getCommandName(config: String) -> String { + package func getCommandName(config: String) -> String { "C." + self.getLLBuildTargetName(config: config) } - public func getLLBuildTargetName(config: String) -> String { + package func getLLBuildTargetName(config: String) -> String { "\(name)-\(config).module" } - public func getLLBuildResourcesCmdName(config: String) -> String { + package func getLLBuildResourcesCmdName(config: String) -> String { "\(name)-\(config).module-resources" } } extension ResolvedProduct { - public func getLLBuildTargetName(config: String) throws -> String { + package func getLLBuildTargetName(config: String) throws -> String { let potentialExecutableTargetName = "\(name)-\(config).exe" let potentialLibraryTargetName = "\(name)-\(config).dylib" @@ -357,7 +357,7 @@ extension ResolvedProduct { } } - public func getCommandName(config: String) throws -> String { + package func getCommandName(config: String) throws -> String { try "C." + self.getLLBuildTargetName(config: config) } } diff --git a/Sources/Build/BuildOperation.swift b/Sources/Build/BuildOperation.swift index 58b5bf2a74d..48ab75d839c 100644 --- a/Sources/Build/BuildOperation.swift +++ b/Sources/Build/BuildOperation.swift @@ -42,7 +42,7 @@ import SwiftDriver package final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildSystem, BuildErrorAdviceProvider { /// The delegate used by the build system. - public weak var delegate: SPMBuildCore.BuildSystemDelegate? + package weak var delegate: SPMBuildCore.BuildSystemDelegate? /// Build parameters for products. let productsBuildParameters: BuildParameters @@ -63,12 +63,12 @@ package final class BuildOperation: PackageStructureDelegate, SPMBuildCore.Build private var buildSystem: SPMLLBuild.BuildSystem? /// If build manifest caching should be enabled. - public let cacheBuildManifest: Bool + package let cacheBuildManifest: Bool /// The build plan that was computed, if any. - public private(set) var _buildPlan: BuildPlan? + package private(set) var _buildPlan: BuildPlan? - public var buildPlan: SPMBuildCore.BuildPlan { + package var buildPlan: SPMBuildCore.BuildPlan { get throws { if let buildPlan = _buildPlan { return buildPlan @@ -96,7 +96,7 @@ package final class BuildOperation: PackageStructureDelegate, SPMBuildCore.Build /// ObservabilityScope with which to emit diagnostics. private let observabilityScope: ObservabilityScope - public var builtTestProducts: [BuiltTestProduct] { + package var builtTestProducts: [BuiltTestProduct] { (try? getBuildDescription())?.builtTestProducts ?? [] } @@ -112,7 +112,7 @@ package final class BuildOperation: PackageStructureDelegate, SPMBuildCore.Build /// Map of root package identities by target names which are declared in them. private let rootPackageIdentityByTargetName: [String: PackageIdentity] - public init( + package init( productsBuildParameters: BuildParameters, toolsBuildParameters: BuildParameters, cacheBuildManifest: Bool, @@ -149,7 +149,7 @@ package final class BuildOperation: PackageStructureDelegate, SPMBuildCore.Build self.observabilityScope = observabilityScope.makeChildScope(description: "Build Operation") } - public func getPackageGraph() throws -> ModulesGraph { + package func getPackageGraph() throws -> ModulesGraph { try self.packageGraph.memoize { try self.packageGraphLoader() } @@ -159,7 +159,7 @@ package final class BuildOperation: PackageStructureDelegate, SPMBuildCore.Build /// /// This will try skip build planning if build manifest caching is enabled /// and the package structure hasn't changed. - public func getBuildDescription(subset: BuildSubset? = nil) throws -> BuildDescription { + package func getBuildDescription(subset: BuildSubset? = nil) throws -> BuildDescription { return try self.buildDescription.memoize { if self.cacheBuildManifest { do { @@ -187,12 +187,12 @@ package final class BuildOperation: PackageStructureDelegate, SPMBuildCore.Build } } - public func getBuildManifest() throws -> LLBuildManifest { + package func getBuildManifest() throws -> LLBuildManifest { return try self.plan().manifest } /// Cancel the active build operation. - public func cancel(deadline: DispatchTime) throws { + package func cancel(deadline: DispatchTime) throws { buildSystem?.cancel() } @@ -336,7 +336,7 @@ package final class BuildOperation: PackageStructureDelegate, SPMBuildCore.Build } /// Perform a build using the given build description and subset. - public func build(subset: BuildSubset) throws { + package func build(subset: BuildSubset) throws { guard !self.productsBuildParameters.shouldSkipBuilding else { return } @@ -784,7 +784,7 @@ package final class BuildOperation: PackageStructureDelegate, SPMBuildCore.Build } } - public func provideBuildErrorAdvice(for target: String, command: String, message: String) -> String? { + package func provideBuildErrorAdvice(for target: String, command: String, message: String) -> String? { // Find the target for which the error was emitted. If we don't find it, we can't give any advice. guard let _ = self._buildPlan?.targets.first(where: { $0.target.name == target }) else { return nil } @@ -808,7 +808,7 @@ package final class BuildOperation: PackageStructureDelegate, SPMBuildCore.Build return nil } - public func packageStructureChanged() -> Bool { + package func packageStructureChanged() -> Bool { do { _ = try self.plan() } @@ -824,7 +824,7 @@ package final class BuildOperation: PackageStructureDelegate, SPMBuildCore.Build } extension BuildOperation { - public struct PluginConfiguration { + package struct PluginConfiguration { /// Entity responsible for compiling and running plugin scripts. let scriptRunner: PluginScriptRunner @@ -834,7 +834,7 @@ extension BuildOperation { /// Whether to sandbox commands from build tool plugins. let disableSandbox: Bool - public init(scriptRunner: PluginScriptRunner, workDirectory: AbsolutePath, disableSandbox: Bool) { + package init(scriptRunner: PluginScriptRunner, workDirectory: AbsolutePath, disableSandbox: Bool) { self.scriptRunner = scriptRunner self.workDirectory = workDirectory self.disableSandbox = disableSandbox diff --git a/Sources/Build/BuildOperationBuildSystemDelegateHandler.swift b/Sources/Build/BuildOperationBuildSystemDelegateHandler.swift index dc1cdba734b..2341eac5466 100644 --- a/Sources/Build/BuildOperationBuildSystemDelegateHandler.swift +++ b/Sources/Build/BuildOperationBuildSystemDelegateHandler.swift @@ -202,7 +202,7 @@ final class TestDiscoveryCommand: CustomLLBuildCommand, TestBuildCommand { } extension TestEntryPointTool { - public static func mainFileName(for library: BuildParameters.Testing.Library) -> String { + package static func mainFileName(for library: BuildParameters.Testing.Library) -> String { "runner-\(library).swift" } } @@ -321,10 +321,10 @@ private final class InProcessTool: Tool { } /// Contains the description of the build that is needed during the execution. -public struct BuildDescription: Codable { - public typealias CommandName = String - public typealias TargetName = String - public typealias CommandLineFlag = String +package struct BuildDescription: Codable { + package typealias CommandName = String + package typealias TargetName = String + package typealias CommandLineFlag = String /// The Swift compiler invocation targets. let swiftCommands: [LLBuildManifest.CmdName: SwiftCompilerTool] @@ -358,12 +358,12 @@ public struct BuildDescription: Codable { let generatedSourceTargetSet: Set /// The built test products. - public let builtTestProducts: [BuiltTestProduct] + package let builtTestProducts: [BuiltTestProduct] /// Distilled information about any plugins defined in the package. let pluginDescriptions: [PluginDescription] - public init( + package init( plan: BuildPlan, swiftCommands: [LLBuildManifest.CmdName: SwiftCompilerTool], swiftFrontendCommands: [LLBuildManifest.CmdName: SwiftFrontendTool], @@ -420,13 +420,13 @@ public struct BuildDescription: Codable { self.pluginDescriptions = pluginDescriptions } - public func write(fileSystem: Basics.FileSystem, path: AbsolutePath) throws { + package func write(fileSystem: Basics.FileSystem, path: AbsolutePath) throws { let encoder = JSONEncoder.makeWithDefaults() let data = try encoder.encode(self) try fileSystem.writeFileContents(path, bytes: ByteString(data)) } - public static func load(fileSystem: Basics.FileSystem, path: AbsolutePath) throws -> BuildDescription { + package static func load(fileSystem: Basics.FileSystem, path: AbsolutePath) throws -> BuildDescription { let contents: Data = try fileSystem.readFileContents(path) let decoder = JSONDecoder.makeWithDefaults() return try decoder.decode(BuildDescription.self, from: contents) @@ -434,14 +434,14 @@ public struct BuildDescription: Codable { } /// A provider of advice about build errors. -public protocol BuildErrorAdviceProvider { +package protocol BuildErrorAdviceProvider { /// Invoked after a command fails and an error message is detected in the output. Should return a string containing /// advice or additional information, if any, based on the build plan. func provideBuildErrorAdvice(for target: String, command: String, message: String) -> String? } /// The context available during build execution. -public final class BuildExecutionContext { +package final class BuildExecutionContext { /// Build parameters for products. let productsBuildParameters: BuildParameters @@ -464,7 +464,7 @@ public final class BuildExecutionContext { let observabilityScope: ObservabilityScope - public init( + package init( productsBuildParameters: BuildParameters, toolsBuildParameters: BuildParameters, buildDescription: BuildDescription? = nil, @@ -590,7 +590,7 @@ final class WriteAuxiliaryFileCommand: CustomLLBuildCommand { } } -public protocol PackageStructureDelegate { +package protocol PackageStructureDelegate { func packageStructureChanged() -> Bool } diff --git a/Sources/Build/BuildPlan/BuildPlan.swift b/Sources/Build/BuildPlan/BuildPlan.swift index f9ef7683c8d..091fb78280f 100644 --- a/Sources/Build/BuildPlan/BuildPlan.swift +++ b/Sources/Build/BuildPlan/BuildPlan.swift @@ -91,7 +91,7 @@ extension [String] { extension BuildParameters { /// Returns the directory to be used for module cache. - public var moduleCache: AbsolutePath { + package var moduleCache: AbsolutePath { get throws { // FIXME: We use this hack to let swiftpm's functional test use shared // cache so it doesn't become painfully slow. @@ -128,7 +128,7 @@ extension BuildParameters { } /// Computes the target triple arguments for a given resolved target. - public func targetTripleArgs(for target: ResolvedTarget) throws -> [String] { + package func targetTripleArgs(for target: ResolvedTarget) throws -> [String] { var args = ["-target"] // Compute the triple string for Darwin platform using the platform version. @@ -164,11 +164,11 @@ extension BuildParameters { /// A build plan for a package graph. public class BuildPlan: SPMBuildCore.BuildPlan { - public enum Error: Swift.Error, CustomStringConvertible, Equatable { + package enum Error: Swift.Error, CustomStringConvertible, Equatable { /// There is no buildable target in the graph. case noBuildableTarget - public var description: String { + package var description: String { switch self { case .noBuildableTarget: return """ @@ -196,20 +196,20 @@ public class BuildPlan: SPMBuildCore.BuildPlan { } /// The package graph. - public let graph: ModulesGraph + package let graph: ModulesGraph /// The target build description map. - public let targetMap: [ResolvedTarget.ID: TargetBuildDescription] + package let targetMap: [ResolvedTarget.ID: TargetBuildDescription] /// The product build description map. - public let productMap: [ResolvedProduct.ID: ProductBuildDescription] + package let productMap: [ResolvedProduct.ID: ProductBuildDescription] /// The plugin descriptions. Plugins are represented in the package graph /// as targets, but they are not directly included in the build graph. - public let pluginDescriptions: [PluginDescription] + package let pluginDescriptions: [PluginDescription] /// The build targets. - public var targets: AnySequence { + package var targets: AnySequence { AnySequence(self.targetMap.values) } @@ -219,11 +219,11 @@ public class BuildPlan: SPMBuildCore.BuildPlan { } /// The results of invoking any build tool plugins used by targets in this build. - public let buildToolPluginInvocationResults: [ResolvedTarget.ID: [BuildToolPluginInvocationResult]] + package let buildToolPluginInvocationResults: [ResolvedTarget.ID: [BuildToolPluginInvocationResult]] /// The results of running any prebuild commands for the targets in this build. This includes any derived /// source files as well as directories to which any changes should cause us to reevaluate the build plan. - public let prebuildCommandResults: [ResolvedTarget.ID: [PrebuildCommandResult]] + package let prebuildCommandResults: [ResolvedTarget.ID: [PrebuildCommandResult]] package private(set) var derivedTestTargetsMap: [ResolvedProduct.ID: [ResolvedTarget]] = [:] diff --git a/Sources/Build/ClangSupport.swift b/Sources/Build/ClangSupport.swift index af5fd8581a5..7055b3119dc 100644 --- a/Sources/Build/ClangSupport.swift +++ b/Sources/Build/ClangSupport.swift @@ -14,7 +14,7 @@ import Basics import Foundation import PackageModel -public enum ClangSupport { +package enum ClangSupport { private struct Feature: Decodable { let name: String let value: [String]? @@ -26,7 +26,7 @@ public enum ClangSupport { private static var cachedFeatures = ThreadSafeBox() - public static func supportsFeature(name: String, toolchain: PackageModel.Toolchain) throws -> Bool { + package static func supportsFeature(name: String, toolchain: PackageModel.Toolchain) throws -> Bool { let features = try cachedFeatures.memoize { let clangPath = try toolchain.getClangCompiler() let featuresPath = clangPath.parentDirectory.parentDirectory.appending(components: ["share", "clang", "features.json"]) diff --git a/Sources/Build/SwiftCompilerOutputParser.swift b/Sources/Build/SwiftCompilerOutputParser.swift index 86b118b2be0..65024db7618 100644 --- a/Sources/Build/SwiftCompilerOutputParser.swift +++ b/Sources/Build/SwiftCompilerOutputParser.swift @@ -16,26 +16,26 @@ import class TSCUtility.JSONMessageStreamingParser import protocol TSCUtility.JSONMessageStreamingParserDelegate /// Represents a message output by the Swift compiler in JSON output mode. -public struct SwiftCompilerMessage { - public enum Kind { - public struct Output { - public let type: String - public let path: String +package struct SwiftCompilerMessage { + package enum Kind { + package struct Output { + package let type: String + package let path: String - public init(type: String, path: String) { + package init(type: String, path: String) { self.type = type self.path = path } } - public struct BeganInfo { - public let pid: Int - public let inputs: [String] - public let outputs: [Output]? - public let commandExecutable: String - public let commandArguments: [String] + package struct BeganInfo { + package let pid: Int + package let inputs: [String] + package let outputs: [Output]? + package let commandExecutable: String + package let commandArguments: [String] - public init( + package init( pid: Int, inputs: [String], outputs: [Output]?, @@ -50,21 +50,21 @@ public struct SwiftCompilerMessage { } } - public struct SkippedInfo { - public let inputs: [String] - public let outputs: [Output]? + package struct SkippedInfo { + package let inputs: [String] + package let outputs: [Output]? - public init(inputs: [String], outputs: [SwiftCompilerMessage.Kind.Output]) { + package init(inputs: [String], outputs: [SwiftCompilerMessage.Kind.Output]) { self.inputs = inputs self.outputs = outputs } } - public struct OutputInfo { - public let pid: Int - public let output: String? + package struct OutputInfo { + package let pid: Int + package let output: String? - public init(pid: Int, output: String?) { + package init(pid: Int, output: String?) { self.pid = pid self.output = output } @@ -77,17 +77,17 @@ public struct SwiftCompilerMessage { case unparsableOutput(String) } - public let name: String - public let kind: Kind + package let name: String + package let kind: Kind - public init(name: String, kind: SwiftCompilerMessage.Kind) { + package init(name: String, kind: SwiftCompilerMessage.Kind) { self.name = name self.kind = kind } } /// Protocol for the parser delegate to get notified of parsing events. -public protocol SwiftCompilerOutputParserDelegate: AnyObject { +package protocol SwiftCompilerOutputParserDelegate: AnyObject { /// Called for each message parsed. func swiftCompilerOutputParser(_ parser: SwiftCompilerOutputParser, didParse message: SwiftCompilerMessage) @@ -97,7 +97,7 @@ public protocol SwiftCompilerOutputParserDelegate: AnyObject { } /// Parser for the Swift compiler JSON output mode. -public final class SwiftCompilerOutputParser { +package final class SwiftCompilerOutputParser { /// The underlying JSON message parser. private var jsonParser: JSONMessageStreamingParser! @@ -106,16 +106,16 @@ public final class SwiftCompilerOutputParser { private var hasFailed: Bool /// Name of the target the compiler is compiling. - public let targetName: String + package let targetName: String /// Delegate to notify of parsing events. - public weak var delegate: SwiftCompilerOutputParserDelegate? + package weak var delegate: SwiftCompilerOutputParserDelegate? /// Initializes the parser with a delegate to notify of parsing events. /// - Parameters: /// - targetName: The name of the target being built. /// - delegate: Delegate to notify of parsing events. - public init(targetName: String, delegate: SwiftCompilerOutputParserDelegate) { + package init(targetName: String, delegate: SwiftCompilerOutputParserDelegate) { self.hasFailed = false self.targetName = targetName self.delegate = delegate @@ -128,7 +128,7 @@ public final class SwiftCompilerOutputParser { /// Parse the next bytes of the Swift compiler JSON output. /// - Note: If a parsing error is encountered, the delegate will be notified and the parser won't accept any further /// input. - public func parse(bytes: C) where C: Collection, C.Element == UInt8 { + package func parse(bytes: C) where C: Collection, C.Element == UInt8 { guard !hasFailed else { return } @@ -138,7 +138,7 @@ public final class SwiftCompilerOutputParser { } extension SwiftCompilerOutputParser: JSONMessageStreamingParserDelegate { - public func jsonMessageStreamingParser( + package func jsonMessageStreamingParser( _ parser: JSONMessageStreamingParser, didParse message: SwiftCompilerMessage ) { @@ -153,7 +153,7 @@ extension SwiftCompilerOutputParser: JSONMessageStreamingParserDelegate { } } - public func jsonMessageStreamingParser( + package func jsonMessageStreamingParser( _ parser: JSONMessageStreamingParser, didParseRawText text: String ) { @@ -165,7 +165,7 @@ extension SwiftCompilerOutputParser: JSONMessageStreamingParserDelegate { delegate?.swiftCompilerOutputParser(self, didParse: message) } - public func jsonMessageStreamingParser( + package func jsonMessageStreamingParser( _ parser: JSONMessageStreamingParser, didFailWith error: Error ) { @@ -179,7 +179,7 @@ extension SwiftCompilerMessage: Decodable, Equatable { case name } - public init(from decoder: Decoder) throws { + package init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) name = try container.decode(String.self, forKey: .name) kind = try Kind(from: decoder) @@ -191,7 +191,7 @@ extension SwiftCompilerMessage.Kind: Decodable, Equatable { case kind } - public init(from decoder: Decoder) throws { + package init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let kind = try container.decode(String.self, forKey: .kind) switch kind { diff --git a/Sources/Build/TestObservation.swift b/Sources/Build/TestObservation.swift index 98a8f5f8e1e..5258e39454f 100644 --- a/Sources/Build/TestObservation.swift +++ b/Sources/Build/TestObservation.swift @@ -12,7 +12,7 @@ import SPMBuildCore -public func generateTestObservationCode(buildParameters: BuildParameters) -> String { +package func generateTestObservationCode(buildParameters: BuildParameters) -> String { guard buildParameters.triple.supportsTestSummary else { return "" } diff --git a/Sources/LLBuildManifest/Command.swift b/Sources/LLBuildManifest/Command.swift index e91928ac102..b37e969e682 100644 --- a/Sources/LLBuildManifest/Command.swift +++ b/Sources/LLBuildManifest/Command.swift @@ -10,12 +10,12 @@ // //===----------------------------------------------------------------------===// -public struct Command { +package struct Command { /// The name of the command. - public var name: String + package var name: String /// The tool used for this command. - public var tool: ToolProtocol + package var tool: ToolProtocol init(name: String, tool: ToolProtocol) { self.name = name diff --git a/Sources/LLBuildManifest/LLBuildManifest.swift b/Sources/LLBuildManifest/LLBuildManifest.swift index 1c67c8c8209..6800ba87c40 100644 --- a/Sources/LLBuildManifest/LLBuildManifest.swift +++ b/Sources/LLBuildManifest/LLBuildManifest.swift @@ -15,14 +15,14 @@ import Foundation import class TSCBasic.Process -public protocol AuxiliaryFileType { +package protocol AuxiliaryFileType { static var name: String { get } static func getFileContents(inputs: [Node]) throws -> String } -public enum WriteAuxiliary { - public static let fileTypes: [AuxiliaryFileType.Type] = [ +package enum WriteAuxiliary { + package static let fileTypes: [AuxiliaryFileType.Type] = [ EntitlementPlist.self, LinkFileList.self, SourcesFileList.self, @@ -30,14 +30,14 @@ public enum WriteAuxiliary { XCTestInfoPlist.self ] - public struct EntitlementPlist: AuxiliaryFileType { - public static let name = "entitlement-plist" + package struct EntitlementPlist: AuxiliaryFileType { + package static let name = "entitlement-plist" - public static func computeInputs(entitlement: String) -> [Node] { + package static func computeInputs(entitlement: String) -> [Node] { [.virtual(Self.name), .virtual(entitlement)] } - public static func getFileContents(inputs: [Node]) throws -> String { + package static func getFileContents(inputs: [Node]) throws -> String { guard let entitlementName = inputs.last?.extractedVirtualNodeName else { throw Error.undefinedEntitlementName } @@ -54,15 +54,15 @@ public enum WriteAuxiliary { } } - public struct LinkFileList: AuxiliaryFileType { - public static let name = "link-file-list" + package struct LinkFileList: AuxiliaryFileType { + package static let name = "link-file-list" // FIXME: We should extend the `InProcessTool` support to allow us to specify these in a typed way, but today we have to flatten all the inputs into a generic `Node` array (rdar://109844243). - public static func computeInputs(objects: [AbsolutePath]) -> [Node] { + package static func computeInputs(objects: [AbsolutePath]) -> [Node] { return [.virtual(Self.name)] + objects.map { Node.file($0) } } - public static func getFileContents(inputs: [Node]) throws -> String { + package static func getFileContents(inputs: [Node]) throws -> String { let objects = inputs.compactMap { if $0.kind == .file { return $0.name @@ -84,14 +84,14 @@ public enum WriteAuxiliary { } } - public struct SourcesFileList: AuxiliaryFileType { - public static let name = "sources-file-list" + package struct SourcesFileList: AuxiliaryFileType { + package static let name = "sources-file-list" - public static func computeInputs(sources: [AbsolutePath]) -> [Node] { + package static func computeInputs(sources: [AbsolutePath]) -> [Node] { return [.virtual(Self.name)] + sources.map { Node.file($0) } } - public static func getFileContents(inputs: [Node]) throws -> String { + package static func getFileContents(inputs: [Node]) throws -> String { let sources = inputs.compactMap { if $0.kind == .file { return $0.name @@ -110,14 +110,14 @@ public enum WriteAuxiliary { } } - public struct SwiftGetVersion: AuxiliaryFileType { - public static let name = "swift-get-version" + package struct SwiftGetVersion: AuxiliaryFileType { + package static let name = "swift-get-version" - public static func computeInputs(swiftCompilerPath: AbsolutePath) -> [Node] { + package static func computeInputs(swiftCompilerPath: AbsolutePath) -> [Node] { return [.virtual(Self.name), .file(swiftCompilerPath)] } - public static func getFileContents(inputs: [Node]) throws -> String { + package static func getFileContents(inputs: [Node]) throws -> String { guard let swiftCompilerPathString = inputs.first(where: { $0.kind == .file })?.name else { throw Error.unknownSwiftCompilerPath } @@ -130,14 +130,14 @@ public enum WriteAuxiliary { } } - public struct XCTestInfoPlist: AuxiliaryFileType { - public static let name = "xctest-info-plist" + package struct XCTestInfoPlist: AuxiliaryFileType { + package static let name = "xctest-info-plist" - public static func computeInputs(principalClass: String) -> [Node] { + package static func computeInputs(principalClass: String) -> [Node] { return [.virtual(Self.name), .virtual(principalClass)] } - public static func getFileContents(inputs: [Node]) throws -> String { + package static func getFileContents(inputs: [Node]) throws -> String { guard let principalClass = inputs.last?.extractedVirtualNodeName else { throw Error.undefinedPrincipalClass } @@ -161,23 +161,23 @@ public enum WriteAuxiliary { } } -public struct LLBuildManifest { - public typealias TargetName = String - public typealias CmdName = String +package struct LLBuildManifest { + package typealias TargetName = String + package typealias CmdName = String /// The targets in the manifest. - public private(set) var targets: [TargetName: Target] = [:] + package private(set) var targets: [TargetName: Target] = [:] /// The commands in the manifest. - public private(set) var commands: [CmdName: Command] = [:] + package private(set) var commands: [CmdName: Command] = [:] /// The default target to build. - public var defaultTarget: String = "" + package var defaultTarget: String = "" - public init() { + package init() { } - public func getCmdToolMap(kind: T.Type) -> [CmdName: T] { + package func getCmdToolMap(kind: T.Type) -> [CmdName: T] { var result = [CmdName: T]() for (cmdName, cmd) in commands { if let tool = cmd.tool as? T { @@ -187,16 +187,16 @@ public struct LLBuildManifest { return result } - public mutating func createTarget(_ name: TargetName) { + package mutating func createTarget(_ name: TargetName) { guard !targets.keys.contains(name) else { return } targets[name] = Target(name: name, nodes: []) } - public mutating func addNode(_ node: Node, toTarget target: TargetName) { + package mutating func addNode(_ node: Node, toTarget target: TargetName) { targets[target, default: Target(name: target, nodes: [])].nodes.append(node) } - public mutating func addPhonyCmd( + package mutating func addPhonyCmd( name: String, inputs: [Node], outputs: [Node] @@ -206,7 +206,7 @@ public struct LLBuildManifest { commands[name] = Command(name: name, tool: tool) } - public mutating func addTestDiscoveryCmd( + package mutating func addTestDiscoveryCmd( name: String, inputs: [Node], outputs: [Node] @@ -216,7 +216,7 @@ public struct LLBuildManifest { commands[name] = Command(name: name, tool: tool) } - public mutating func addTestEntryPointCmd( + package mutating func addTestEntryPointCmd( name: String, inputs: [Node], outputs: [Node] @@ -226,7 +226,7 @@ public struct LLBuildManifest { commands[name] = Command(name: name, tool: tool) } - public mutating func addCopyCmd( + package mutating func addCopyCmd( name: String, inputs: [Node], outputs: [Node] @@ -236,14 +236,14 @@ public struct LLBuildManifest { commands[name] = Command(name: name, tool: tool) } - public mutating func addEntitlementPlistCommand(entitlement: String, outputPath: AbsolutePath) { + package mutating func addEntitlementPlistCommand(entitlement: String, outputPath: AbsolutePath) { let inputs = WriteAuxiliary.EntitlementPlist.computeInputs(entitlement: entitlement) let tool = WriteAuxiliaryFile(inputs: inputs, outputFilePath: outputPath) let name = outputPath.pathString commands[name] = Command(name: name, tool: tool) } - public mutating func addWriteLinkFileListCommand( + package mutating func addWriteLinkFileListCommand( objects: [AbsolutePath], linkFileListPath: AbsolutePath ) { @@ -253,7 +253,7 @@ public struct LLBuildManifest { commands[name] = Command(name: name, tool: tool) } - public mutating func addWriteSourcesFileListCommand( + package mutating func addWriteSourcesFileListCommand( sources: [AbsolutePath], sourcesFileListPath: AbsolutePath ) { @@ -263,7 +263,7 @@ public struct LLBuildManifest { commands[name] = Command(name: name, tool: tool) } - public mutating func addSwiftGetVersionCommand( + package mutating func addSwiftGetVersionCommand( swiftCompilerPath: AbsolutePath, swiftVersionFilePath: AbsolutePath ) { @@ -273,14 +273,14 @@ public struct LLBuildManifest { commands[name] = Command(name: name, tool: tool) } - public mutating func addWriteInfoPlistCommand(principalClass: String, outputPath: AbsolutePath) { + package mutating func addWriteInfoPlistCommand(principalClass: String, outputPath: AbsolutePath) { let inputs = WriteAuxiliary.XCTestInfoPlist.computeInputs(principalClass: principalClass) let tool = WriteAuxiliaryFile(inputs: inputs, outputFilePath: outputPath) let name = outputPath.pathString commands[name] = Command(name: name, tool: tool) } - public mutating func addPkgStructureCmd( + package mutating func addPkgStructureCmd( name: String, inputs: [Node], outputs: [Node] @@ -290,7 +290,7 @@ public struct LLBuildManifest { commands[name] = Command(name: name, tool: tool) } - public mutating func addShellCmd( + package mutating func addShellCmd( name: String, description: String, inputs: [Node], @@ -313,7 +313,7 @@ public struct LLBuildManifest { commands[name] = Command(name: name, tool: tool) } - public mutating func addSwiftFrontendCmd( + package mutating func addSwiftFrontendCmd( name: String, moduleName: String, packageName: String, @@ -333,7 +333,7 @@ public struct LLBuildManifest { commands[name] = Command(name: name, tool: tool) } - public mutating func addClangCmd( + package mutating func addClangCmd( name: String, description: String, inputs: [Node], @@ -352,7 +352,7 @@ public struct LLBuildManifest { commands[name] = Command(name: name, tool: tool) } - public mutating func addSwiftCmd( + package mutating func addSwiftCmd( name: String, inputs: [Node], outputs: [Node], diff --git a/Sources/LLBuildManifest/LLBuildManifestWriter.swift b/Sources/LLBuildManifest/LLBuildManifestWriter.swift index b85a65c5098..2177778a2b9 100644 --- a/Sources/LLBuildManifest/LLBuildManifestWriter.swift +++ b/Sources/LLBuildManifest/LLBuildManifestWriter.swift @@ -14,7 +14,7 @@ import Basics private let namesToExclude = [".git", ".build"] -public struct LLBuildManifestWriter { +package struct LLBuildManifestWriter { private let manifest: LLBuildManifest // FIXME: since JSON is a superset of YAML and we don't need to parse these manifests, // we should just use `JSONEncoder` instead. @@ -38,7 +38,7 @@ public struct LLBuildManifestWriter { self.render(commands: manifest.commands) } - public static func write(_ manifest: LLBuildManifest, at path: AbsolutePath, fileSystem: FileSystem) throws { + package static func write(_ manifest: LLBuildManifest, at path: AbsolutePath, fileSystem: FileSystem) throws { let writer = LLBuildManifestWriter(manifest: manifest) try fileSystem.writeFileContents(path, string: writer.buffer) @@ -125,66 +125,66 @@ public struct LLBuildManifestWriter { } } -public struct ManifestToolStream { +package struct ManifestToolStream { fileprivate var buffer = "" - public subscript(key: String) -> Int { + package subscript(key: String) -> Int { get { fatalError() } set { self.buffer += " \(key): \(newValue.description.asJSON)\n" } } - public subscript(key: String) -> String { + package subscript(key: String) -> String { get { fatalError() } set { self.buffer += " \(key): \(newValue.asJSON)\n" } } - public subscript(key: String) -> ToolProtocol { + package subscript(key: String) -> ToolProtocol { get { fatalError() } set { self.buffer += " \(key): \(type(of: newValue).name)\n" } } - public subscript(key: String) -> AbsolutePath { + package subscript(key: String) -> AbsolutePath { get { fatalError() } set { self.buffer += " \(key): \(newValue.pathString.asJSON)\n" } } - public subscript(key: String) -> [AbsolutePath] { + package subscript(key: String) -> [AbsolutePath] { get { fatalError() } set { self.buffer += " \(key): \(newValue.map(\.pathString).asJSON)\n" } } - public subscript(key: String) -> [Node] { + package subscript(key: String) -> [Node] { get { fatalError() } set { self.buffer += " \(key): \(newValue.map(\.encodingName).asJSON)\n" } } - public subscript(key: String) -> Bool { + package subscript(key: String) -> Bool { get { fatalError() } set { self.buffer += " \(key): \(newValue.description)\n" } } - public subscript(key: String) -> [String] { + package subscript(key: String) -> [String] { get { fatalError() } set { self.buffer += " \(key): \(newValue.asJSON)\n" } } - public subscript(key: String) -> [String: String] { + package subscript(key: String) -> [String: String] { get { fatalError() } set { self.buffer += " \(key):\n" diff --git a/Sources/LLBuildManifest/Node.swift b/Sources/LLBuildManifest/Node.swift index 9dd898b6682..6ee8aabefcf 100644 --- a/Sources/LLBuildManifest/Node.swift +++ b/Sources/LLBuildManifest/Node.swift @@ -12,8 +12,8 @@ import Basics -public struct Node: Hashable, Codable { - public enum Kind: String, Hashable, Codable { +package struct Node: Hashable, Codable { + package enum Kind: String, Hashable, Codable { case virtual case file case directory @@ -26,10 +26,10 @@ public struct Node: Hashable, Codable { } /// The name used to identify the node. - public var name: String + package var name: String /// The kind of node. - public var kind: Kind + package var kind: Kind let attributes: Attributes? @@ -40,12 +40,12 @@ public struct Node: Hashable, Codable { } /// Extracts `name` property if this node was constructed as `Node//virtual`. - public var extractedVirtualNodeName: String { + package var extractedVirtualNodeName: String { precondition(kind == .virtual) return String(self.name.dropFirst().dropLast()) } - public static func virtual(_ name: String, isCommandTimestamp: Bool = false) -> Node { + package static func virtual(_ name: String, isCommandTimestamp: Bool = false) -> Node { precondition(name.first != "<" && name.last != ">", "<> will be inserted automatically") return Node( name: "<" + name + ">", @@ -54,11 +54,11 @@ public struct Node: Hashable, Codable { ) } - public static func file(_ name: AbsolutePath) -> Node { + package static func file(_ name: AbsolutePath) -> Node { Node(name: name.pathString, kind: .file) } - public static func file(_ name: AbsolutePath, isMutated: Bool) -> Node { + package static func file(_ name: AbsolutePath, isMutated: Bool) -> Node { Node( name: name.pathString, kind: .file, @@ -66,25 +66,25 @@ public struct Node: Hashable, Codable { ) } - public static func directory(_ name: AbsolutePath) -> Node { + package static func directory(_ name: AbsolutePath) -> Node { Node(name: name.pathString, kind: .directory) } - public static func directoryStructure(_ name: AbsolutePath) -> Node { + package static func directoryStructure(_ name: AbsolutePath) -> Node { Node(name: name.pathString, kind: .directoryStructure) } } extension Array where Element == Node { - public mutating func append(file path: AbsolutePath) { + package mutating func append(file path: AbsolutePath) { self.append(.file(path)) } - public mutating func append(directory path: AbsolutePath) { + package mutating func append(directory path: AbsolutePath) { self.append(.directory(path)) } - public mutating func append(directoryStructure path: AbsolutePath) { + package mutating func append(directoryStructure path: AbsolutePath) { self.append(.directoryStructure(path)) } } diff --git a/Sources/LLBuildManifest/Target.swift b/Sources/LLBuildManifest/Target.swift index 292dd520b39..263c54150e7 100644 --- a/Sources/LLBuildManifest/Target.swift +++ b/Sources/LLBuildManifest/Target.swift @@ -10,14 +10,14 @@ // //===----------------------------------------------------------------------===// -public struct Target { +package struct Target { /// The name of the target. - public var name: String + package var name: String /// The list of nodes that should be computed to build this target. - public var nodes: [Node] + package var nodes: [Node] - public init(name: String, nodes: [Node]) { + package init(name: String, nodes: [Node]) { self.name = name self.nodes = nodes } diff --git a/Sources/LLBuildManifest/Tools.swift b/Sources/LLBuildManifest/Tools.swift index 7a6172fe2fb..9fabe80aee6 100644 --- a/Sources/LLBuildManifest/Tools.swift +++ b/Sources/LLBuildManifest/Tools.swift @@ -13,7 +13,7 @@ import Basics import class Foundation.ProcessInfo -public protocol ToolProtocol: Codable { +package protocol ToolProtocol: Codable { /// The name of the tool. static var name: String { get } @@ -31,16 +31,16 @@ public protocol ToolProtocol: Codable { } extension ToolProtocol { - public var alwaysOutOfDate: Bool { return false } + package var alwaysOutOfDate: Bool { return false } - public func write(to stream: inout ManifestToolStream) {} + package func write(to stream: inout ManifestToolStream) {} } -public struct PhonyTool: ToolProtocol { - public static let name: String = "phony" +package struct PhonyTool: ToolProtocol { + package static let name: String = "phony" - public var inputs: [Node] - public var outputs: [Node] + package var inputs: [Node] + package var outputs: [Node] init(inputs: [Node], outputs: [Node]) { self.inputs = inputs @@ -48,12 +48,12 @@ public struct PhonyTool: ToolProtocol { } } -public struct TestDiscoveryTool: ToolProtocol { - public static let name: String = "test-discovery-tool" - public static let mainFileName: String = "all-discovered-tests.swift" +package struct TestDiscoveryTool: ToolProtocol { + package static let name: String = "test-discovery-tool" + package static let mainFileName: String = "all-discovered-tests.swift" - public var inputs: [Node] - public var outputs: [Node] + package var inputs: [Node] + package var outputs: [Node] init(inputs: [Node], outputs: [Node]) { self.inputs = inputs @@ -61,11 +61,11 @@ public struct TestDiscoveryTool: ToolProtocol { } } -public struct TestEntryPointTool: ToolProtocol { - public static let name: String = "test-entry-point-tool" +package struct TestEntryPointTool: ToolProtocol { + package static let name: String = "test-entry-point-tool" - public var inputs: [Node] - public var outputs: [Node] + package var inputs: [Node] + package var outputs: [Node] init(inputs: [Node], outputs: [Node]) { self.inputs = inputs @@ -73,18 +73,18 @@ public struct TestEntryPointTool: ToolProtocol { } } -public struct CopyTool: ToolProtocol { - public static let name: String = "copy-tool" +package struct CopyTool: ToolProtocol { + package static let name: String = "copy-tool" - public var inputs: [Node] - public var outputs: [Node] + package var inputs: [Node] + package var outputs: [Node] init(inputs: [Node], outputs: [Node]) { self.inputs = inputs self.outputs = outputs } - public func write(to stream: inout ManifestToolStream) { + package func write(to stream: inout ManifestToolStream) { stream["description"] = "Copying \(inputs[0].name)" } } @@ -93,33 +93,33 @@ public struct CopyTool: ToolProtocol { /// that requires regenerating the build manifest file. This allows us to skip a lot of /// redundant work (package graph loading, build planning, manifest generation) during /// incremental builds. -public struct PackageStructureTool: ToolProtocol { - public static let name: String = "package-structure-tool" +package struct PackageStructureTool: ToolProtocol { + package static let name: String = "package-structure-tool" - public var inputs: [Node] - public var outputs: [Node] + package var inputs: [Node] + package var outputs: [Node] init(inputs: [Node], outputs: [Node]) { self.inputs = inputs self.outputs = outputs } - public func write(to stream: inout ManifestToolStream) { + package func write(to stream: inout ManifestToolStream) { stream["description"] = "Planning build" stream["allow-missing-inputs"] = true } } -public struct ShellTool: ToolProtocol { - public static let name: String = "shell" +package struct ShellTool: ToolProtocol { + package static let name: String = "shell" - public var description: String - public var inputs: [Node] - public var outputs: [Node] - public var arguments: [String] - public var environment: EnvironmentVariables - public var workingDirectory: String? - public var allowMissingInputs: Bool + package var description: String + package var inputs: [Node] + package var outputs: [Node] + package var arguments: [String] + package var environment: EnvironmentVariables + package var workingDirectory: String? + package var allowMissingInputs: Bool init( description: String, @@ -139,7 +139,7 @@ public struct ShellTool: ToolProtocol { self.allowMissingInputs = allowMissingInputs } - public func write(to stream: inout ManifestToolStream) { + package func write(to stream: inout ManifestToolStream) { stream["description"] = description stream["args"] = arguments if !environment.isEmpty { @@ -154,36 +154,36 @@ public struct ShellTool: ToolProtocol { } } -public struct WriteAuxiliaryFile: Equatable, ToolProtocol { - public static let name: String = "write-auxiliary-file" +package struct WriteAuxiliaryFile: Equatable, ToolProtocol { + package static let name: String = "write-auxiliary-file" - public let inputs: [Node] + package let inputs: [Node] private let outputFilePath: AbsolutePath - public let alwaysOutOfDate: Bool + package let alwaysOutOfDate: Bool - public init(inputs: [Node], outputFilePath: AbsolutePath, alwaysOutOfDate: Bool = false) { + package init(inputs: [Node], outputFilePath: AbsolutePath, alwaysOutOfDate: Bool = false) { self.inputs = inputs self.outputFilePath = outputFilePath self.alwaysOutOfDate = alwaysOutOfDate } - public var outputs: [Node] { + package var outputs: [Node] { return [.file(outputFilePath)] } - public func write(to stream: inout ManifestToolStream) { + package func write(to stream: inout ManifestToolStream) { stream["description"] = "Write auxiliary file \(outputFilePath.pathString)" } } -public struct ClangTool: ToolProtocol { - public static let name: String = "clang" +package struct ClangTool: ToolProtocol { + package static let name: String = "clang" - public var description: String - public var inputs: [Node] - public var outputs: [Node] - public var arguments: [String] - public var dependencies: String? + package var description: String + package var inputs: [Node] + package var outputs: [Node] + package var arguments: [String] + package var dependencies: String? init( description: String, @@ -199,7 +199,7 @@ public struct ClangTool: ToolProtocol { self.dependencies = dependencies } - public func write(to stream: inout ManifestToolStream) { + package func write(to stream: inout ManifestToolStream) { stream["description"] = description stream["args"] = arguments if let dependencies { @@ -208,11 +208,11 @@ public struct ClangTool: ToolProtocol { } } -public struct ArchiveTool: ToolProtocol { - public static let name: String = "archive" +package struct ArchiveTool: ToolProtocol { + package static let name: String = "archive" - public var inputs: [Node] - public var outputs: [Node] + package var inputs: [Node] + package var outputs: [Node] init(inputs: [Node], outputs: [Node]) { self.inputs = inputs @@ -221,14 +221,14 @@ public struct ArchiveTool: ToolProtocol { } /// Swift frontend tool, which maps down to a shell tool. -public struct SwiftFrontendTool: ToolProtocol { - public static let name: String = "shell" +package struct SwiftFrontendTool: ToolProtocol { + package static let name: String = "shell" - public let moduleName: String - public var description: String - public var inputs: [Node] - public var outputs: [Node] - public var arguments: [String] + package let moduleName: String + package var description: String + package var inputs: [Node] + package var outputs: [Node] + package var arguments: [String] init( moduleName: String, @@ -244,33 +244,33 @@ public struct SwiftFrontendTool: ToolProtocol { self.arguments = arguments } - public func write(to stream: inout ManifestToolStream) { + package func write(to stream: inout ManifestToolStream) { ShellTool(description: description, inputs: inputs, outputs: outputs, arguments: arguments).write(to: &stream) } } /// Swift compiler llbuild tool. -public struct SwiftCompilerTool: ToolProtocol { - public static let name: String = "shell" - - public static let numThreads: Int = ProcessInfo.processInfo.activeProcessorCount - - public var inputs: [Node] - public var outputs: [Node] - - public var executable: AbsolutePath - public var moduleName: String - public var moduleAliases: [String: String]? - public var moduleOutputPath: AbsolutePath - public var importPath: AbsolutePath - public var tempsPath: AbsolutePath - public var objects: [AbsolutePath] - public var otherArguments: [String] - public var sources: [AbsolutePath] - public var fileList: AbsolutePath - public var isLibrary: Bool - public var wholeModuleOptimization: Bool - public var outputFileMapPath: AbsolutePath +package struct SwiftCompilerTool: ToolProtocol { + package static let name: String = "shell" + + package static let numThreads: Int = ProcessInfo.processInfo.activeProcessorCount + + package var inputs: [Node] + package var outputs: [Node] + + package var executable: AbsolutePath + package var moduleName: String + package var moduleAliases: [String: String]? + package var moduleOutputPath: AbsolutePath + package var importPath: AbsolutePath + package var tempsPath: AbsolutePath + package var objects: [AbsolutePath] + package var otherArguments: [String] + package var sources: [AbsolutePath] + package var fileList: AbsolutePath + package var isLibrary: Bool + package var wholeModuleOptimization: Bool + package var outputFileMapPath: AbsolutePath init( inputs: [Node], @@ -340,7 +340,7 @@ public struct SwiftCompilerTool: ToolProtocol { return arguments } - public func write(to stream: inout ManifestToolStream) { + package func write(to stream: inout ManifestToolStream) { ShellTool(description: description, inputs: inputs, outputs: outputs, arguments: arguments).write(to: &stream) } } From 8f653ebc19f292ec9b2175d4a9f478096675cefd Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 5 Mar 2024 14:47:03 +0000 Subject: [PATCH 042/159] Replace `ProcessEnv.vars` with `block` w/o API breakage (#7390) This fixes some `ProcessEnv.vars` warnings without more intrusive breaking changes for SwiftPM clients, as previously attempted in https://github.com/apple/swift-package-manager/pull/7364. Also fixed some formatting inconsistencies. --- .../Concurrency/ConcurrencyHelpers.swift | 2 +- Sources/Build/BuildPlan/BuildPlan.swift | 22 ++++++++++++------- Sources/Commands/PackageCommands/Format.swift | 2 +- Sources/Commands/SwiftTestCommand.swift | 4 ++-- Sources/PackageLoading/ManifestLoader.swift | 16 +++++++++++--- Sources/PackageLoading/PkgConfig.swift | 4 ++-- Sources/PackageModel/SwiftSDKs/SwiftSDK.swift | 6 ++--- .../BuildSystem/BuildSystem.swift | 4 ++-- Sources/SourceControl/RepositoryManager.swift | 2 +- .../Workspace/DefaultPluginScriptRunner.swift | 2 +- .../Workspace/Workspace+Configuration.swift | 2 +- Sources/XCBuildSupport/XcodeBuildSystem.swift | 2 +- Tests/CommandsTests/APIDiffTests.swift | 2 +- .../GitHubPackageMetadataProviderTests.swift | 2 +- 14 files changed, 44 insertions(+), 28 deletions(-) diff --git a/Sources/Basics/Concurrency/ConcurrencyHelpers.swift b/Sources/Basics/Concurrency/ConcurrencyHelpers.swift index cc60514fd9a..24330b3e79f 100644 --- a/Sources/Basics/Concurrency/ConcurrencyHelpers.swift +++ b/Sources/Basics/Concurrency/ConcurrencyHelpers.swift @@ -20,7 +20,7 @@ import func TSCBasic.tsc_await public enum Concurrency { public static var maxOperations: Int { - ProcessEnv.vars["SWIFTPM_MAX_CONCURRENT_OPERATIONS"].flatMap(Int.init) ?? ProcessInfo.processInfo + ProcessEnv.block["SWIFTPM_MAX_CONCURRENT_OPERATIONS"].flatMap(Int.init) ?? ProcessInfo.processInfo .activeProcessorCount } } diff --git a/Sources/Build/BuildPlan/BuildPlan.swift b/Sources/Build/BuildPlan/BuildPlan.swift index 091fb78280f..dced6904e49 100644 --- a/Sources/Build/BuildPlan/BuildPlan.swift +++ b/Sources/Build/BuildPlan/BuildPlan.swift @@ -95,7 +95,7 @@ extension BuildParameters { get throws { // FIXME: We use this hack to let swiftpm's functional test use shared // cache so it doesn't become painfully slow. - if let path = ProcessEnv.vars["SWIFTPM_TESTS_MODULECACHE"] { + if let path = ProcessEnv.block["SWIFTPM_TESTS_MODULECACHE"] { return try AbsolutePath(validating: path) } return buildPath.appending("ModuleCache") @@ -158,7 +158,7 @@ extension BuildParameters { /// Returns the scoped view of build settings for a given target. func createScope(for target: ResolvedTarget) -> BuildSettings.Scope { - return BuildSettings.Scope(target.underlying.buildSettings, environment: buildEnvironment) + BuildSettings.Scope(target.underlying.buildSettings, environment: buildEnvironment) } } @@ -446,7 +446,8 @@ public class BuildPlan: SPMBuildCore.BuildPlan { for item in derivedTestTargets { var derivedTestTargets = [item.entryPointTargetBuildDescription.target] - targetMap[item.entryPointTargetBuildDescription.target.id] = .swift(item.entryPointTargetBuildDescription) + targetMap[item.entryPointTargetBuildDescription.target.id] = + .swift(item.entryPointTargetBuildDescription) if let discoveryTargetBuildDescription = item.discoveryTargetBuildDescription { targetMap[discoveryTargetBuildDescription.target.id] = .swift(discoveryTargetBuildDescription) @@ -552,9 +553,9 @@ public class BuildPlan: SPMBuildCore.BuildPlan { } // Add search paths from the system library targets. - for target in graph.reachableTargets { + for target in self.graph.reachableTargets { if let systemLib = target.underlying as? SystemLibraryTarget { - arguments.append(contentsOf: try self.pkgConfig(for: systemLib).cFlags) + try arguments.append(contentsOf: self.pkgConfig(for: systemLib).cFlags) // Add the path to the module map. arguments += ["-I", systemLib.moduleMapPath.parentDirectory.pathString] } @@ -589,7 +590,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan { } // Add search paths from the system library targets. - for target in graph.reachableTargets { + for target in self.graph.reachableTargets { if let systemLib = target.underlying as? SystemLibraryTarget { arguments += try self.pkgConfig(for: systemLib).cFlags } @@ -645,7 +646,12 @@ public class BuildPlan: SPMBuildCore.BuildPlan { extension Basics.Diagnostic { static var swiftBackDeployError: Self { .warning( - "Swift compiler no longer supports statically linking the Swift libraries. They're included in the OS by default starting with macOS Mojave 10.14.4 beta 3. For macOS Mojave 10.14.3 and earlier, there's an optional Swift library package that can be downloaded from \"More Downloads\" for Apple Developers at https://developer.apple.com/download/more/" + """ + Swift compiler no longer supports statically linking the Swift libraries. They're included in the OS by \ + default starting with macOS Mojave 10.14.4 beta 3. For macOS Mojave 10.14.3 and earlier, there's an \ + optional Swift library package that can be downloaded from \"More Downloads\" for Apple Developers at \ + https://developer.apple.com/download/more/ + """ ) } @@ -728,7 +734,7 @@ extension ResolvedProduct { } private var isBinaryOnly: Bool { - return self.targets.filter({ !($0.underlying is BinaryTarget) }).isEmpty + self.targets.filter { !($0.underlying is BinaryTarget) }.isEmpty } private var isPlugin: Bool { diff --git a/Sources/Commands/PackageCommands/Format.swift b/Sources/Commands/PackageCommands/Format.swift index 39140940005..13268b1d9b5 100644 --- a/Sources/Commands/PackageCommands/Format.swift +++ b/Sources/Commands/PackageCommands/Format.swift @@ -37,7 +37,7 @@ extension SwiftPackageCommand { func run(_ swiftCommandState: SwiftCommandState) async throws { // Look for swift-format binary. // FIXME: This should be moved to user toolchain. - let swiftFormatInEnv = lookupExecutablePath(filename: ProcessEnv.vars["SWIFT_FORMAT"]) + let swiftFormatInEnv = lookupExecutablePath(filename: ProcessEnv.block["SWIFT_FORMAT"]) guard let swiftFormat = swiftFormatInEnv ?? Process.findExecutable("swift-format").flatMap(AbsolutePath.init) else { swiftCommandState.observabilityScope.emit(error: "Could not find swift-format in PATH or SWIFT_FORMAT") throw TSCUtility.Diagnostics.fatalError diff --git a/Sources/Commands/SwiftTestCommand.swift b/Sources/Commands/SwiftTestCommand.swift index 58b39b3d1fa..bbccc0c2260 100644 --- a/Sources/Commands/SwiftTestCommand.swift +++ b/Sources/Commands/SwiftTestCommand.swift @@ -933,7 +933,7 @@ final class ParallelTestRunner { // command's result output goes on stdout // ie "swift test" should output to stdout - if ProcessEnv.vars["SWIFTPM_TEST_RUNNER_PROGRESS_BAR"] == "lit" { + if ProcessEnv.block["SWIFTPM_TEST_RUNNER_PROGRESS_BAR"] == "lit" { self.progressAnimation = ProgressAnimation.percent( stream: TSCBasic.stdoutStream, verbose: false, @@ -1292,7 +1292,7 @@ extension TestCommandOptions { /// Returns the test case specifier if overridden in the env. private func skippedTestsOverride(fileSystem: FileSystem) -> TestCaseSpecifier? { - guard let override = ProcessEnv.vars["_SWIFTPM_SKIP_TESTS_LIST"] else { + guard let override = ProcessEnv.block["_SWIFTPM_SKIP_TESTS_LIST"] else { return nil } diff --git a/Sources/PackageLoading/ManifestLoader.swift b/Sources/PackageLoading/ManifestLoader.swift index 3ce676254e0..9424af7e617 100644 --- a/Sources/PackageLoading/ManifestLoader.swift +++ b/Sources/PackageLoading/ManifestLoader.swift @@ -857,7 +857,9 @@ public final class ManifestLoader: ManifestLoaderProtocol { // FIXME: Workaround for the module cache bug that's been haunting Swift CI // - let moduleCachePath = try (ProcessEnv.vars["SWIFTPM_MODULECACHE_OVERRIDE"] ?? ProcessEnv.vars["SWIFTPM_TESTS_MODULECACHE"]).flatMap{ try AbsolutePath(validating: $0) } + let moduleCachePath = try ( + ProcessEnv.block["SWIFTPM_MODULECACHE_OVERRIDE"] ?? + ProcessEnv.block["SWIFTPM_TESTS_MODULECACHE"]).flatMap { try AbsolutePath(validating: $0) } var cmd: [String] = [] cmd += [self.toolchain.swiftCompilerPathForManifests.pathString] @@ -955,7 +957,11 @@ public final class ManifestLoader: ManifestLoaderProtocol { evaluationResult.compilerCommandLine = cmd // Compile the manifest. - TSCBasic.Process.popen(arguments: cmd, environment: self.toolchain.swiftCompilerEnvironment, queue: callbackQueue) { result in + TSCBasic.Process.popen( + arguments: cmd, + environment: self.toolchain.swiftCompilerEnvironment, + queue: callbackQueue + ) { result in dispatchPrecondition(condition: .onQueue(callbackQueue)) var cleanupIfError = DelayableAction(target: tmpDir, action: cleanupTmpDir) @@ -1054,7 +1060,11 @@ public final class ManifestLoader: ManifestLoaderProtocol { #endif let cleanupAfterRunning = cleanupIfError.delay() - TSCBasic.Process.popen(arguments: cmd, environment: environment, queue: callbackQueue) { result in + TSCBasic.Process.popen( + arguments: cmd, + environment: environment, + queue: callbackQueue + ) { result in dispatchPrecondition(condition: .onQueue(callbackQueue)) defer { cleanupAfterRunning.perform() } diff --git a/Sources/PackageLoading/PkgConfig.swift b/Sources/PackageLoading/PkgConfig.swift index cb2a5c539de..2fdb1d60788 100644 --- a/Sources/PackageLoading/PkgConfig.swift +++ b/Sources/PackageLoading/PkgConfig.swift @@ -126,7 +126,7 @@ public struct PkgConfig { private static var envSearchPaths: [AbsolutePath] { get throws { - if let configPath = ProcessEnv.vars["PKG_CONFIG_PATH"] { + if let configPath = ProcessEnv.block["PKG_CONFIG_PATH"] { #if os(Windows) return try configPath.split(separator: ";").map({ try AbsolutePath(validating: String($0)) }) #else @@ -183,7 +183,7 @@ internal struct PkgConfigParser { variables["pcfiledir"] = pcFile.parentDirectory.pathString // Add pc_sysrootdir variable. This is the path of the sysroot directory for pc files. - variables["pc_sysrootdir"] = ProcessEnv.vars["PKG_CONFIG_SYSROOT_DIR"] ?? AbsolutePath.root.pathString + variables["pc_sysrootdir"] = ProcessEnv.block["PKG_CONFIG_SYSROOT_DIR"] ?? AbsolutePath.root.pathString let fileContents: String = try fileSystem.readFileContents(pcFile) for line in fileContents.components(separatedBy: "\n") { diff --git a/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift b/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift index 44bcbac3664..3a3ec0452f3 100644 --- a/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift +++ b/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift @@ -465,10 +465,10 @@ public struct SwiftSDK: Equatable { ) throws -> SwiftSDK { let originalWorkingDirectory = originalWorkingDirectory ?? localFileSystem.currentWorkingDirectory // Select the correct binDir. - if ProcessEnv.vars["SWIFTPM_CUSTOM_BINDIR"] != nil { + if ProcessEnv.block["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"]) + let customBinDir = (ProcessEnv.block["SWIFTPM_CUSTOM_BIN_DIR"] ?? ProcessEnv.block["SWIFTPM_CUSTOM_BINDIR"]) .flatMap { try? AbsolutePath(validating: $0) } let binDir = try customBinDir ?? binDir ?? SwiftSDK.hostBinDir( fileSystem: localFileSystem, @@ -478,7 +478,7 @@ public struct SwiftSDK: Equatable { let sdkPath: AbsolutePath? #if os(macOS) // Get the SDK. - if let value = ProcessEnv.vars["SDKROOT"] { + if let value = ProcessEnv.block["SDKROOT"] { sdkPath = try AbsolutePath(validating: value) } else { // No value in env, so search for it. diff --git a/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift b/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift index 4aa3567473d..d3c736841e5 100644 --- a/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift +++ b/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift @@ -175,8 +175,8 @@ package enum BuildSystemUtilities { /// Returns the build path from the environment, if present. public static func getEnvBuildPath(workingDir: AbsolutePath) throws -> AbsolutePath? { // Don't rely on build path from env for SwiftPM's own tests. - guard ProcessEnv.vars["SWIFTPM_TESTS_MODULECACHE"] == nil else { return nil } - guard let env = ProcessEnv.vars["SWIFTPM_BUILD_DIR"] else { return nil } + guard ProcessEnv.block["SWIFTPM_TESTS_MODULECACHE"] == nil else { return nil } + guard let env = ProcessEnv.block["SWIFTPM_BUILD_DIR"] else { return nil } return try AbsolutePath(validating: env, relativeTo: workingDir) } } diff --git a/Sources/SourceControl/RepositoryManager.swift b/Sources/SourceControl/RepositoryManager.swift index 7c48f8004dd..1891753663f 100644 --- a/Sources/SourceControl/RepositoryManager.swift +++ b/Sources/SourceControl/RepositoryManager.swift @@ -331,7 +331,7 @@ public class RepositoryManager: Cancellable { } // We are expecting handle.repository.url to always be a resolved absolute path. - let shouldCacheLocalPackages = ProcessEnv.vars["SWIFTPM_TESTS_PACKAGECACHE"] == "1" || cacheLocalPackages + let shouldCacheLocalPackages = ProcessEnv.block["SWIFTPM_TESTS_PACKAGECACHE"] == "1" || cacheLocalPackages if let cachePath, !(handle.repository.isLocal && !shouldCacheLocalPackages) { let cachedRepositoryPath = try cachePath.appending(handle.repository.storagePath()) diff --git a/Sources/Workspace/DefaultPluginScriptRunner.swift b/Sources/Workspace/DefaultPluginScriptRunner.swift index be98dcf9f01..5fff93e0915 100644 --- a/Sources/Workspace/DefaultPluginScriptRunner.swift +++ b/Sources/Workspace/DefaultPluginScriptRunner.swift @@ -209,7 +209,7 @@ public struct DefaultPluginScriptRunner: PluginScriptRunner, Cancellable { #endif // Honor any module cache override that's set in the environment. - let moduleCachePath = ProcessEnv.vars["SWIFTPM_MODULECACHE_OVERRIDE"] ?? ProcessEnv.vars["SWIFTPM_TESTS_MODULECACHE"] + let moduleCachePath = ProcessEnv.block["SWIFTPM_MODULECACHE_OVERRIDE"] ?? ProcessEnv.block["SWIFTPM_TESTS_MODULECACHE"] if let moduleCachePath { commandLine += ["-module-cache-path", moduleCachePath] } diff --git a/Sources/Workspace/Workspace+Configuration.swift b/Sources/Workspace/Workspace+Configuration.swift index 103a0f13aeb..773d32301ab 100644 --- a/Sources/Workspace/Workspace+Configuration.swift +++ b/Sources/Workspace/Workspace+Configuration.swift @@ -89,7 +89,7 @@ extension Workspace { public var localMirrorsConfigurationFile: AbsolutePath { get throws { // backwards compatibility - if let customPath = ProcessEnv.vars["SWIFTPM_MIRROR_CONFIG"] { + if let customPath = ProcessEnv.block["SWIFTPM_MIRROR_CONFIG"] { return try AbsolutePath(validating: customPath) } return DefaultLocations.mirrorsConfigurationFile(at: self.localConfigurationDirectory) diff --git a/Sources/XCBuildSupport/XcodeBuildSystem.swift b/Sources/XCBuildSupport/XcodeBuildSystem.swift index 99538864353..be247e68e85 100644 --- a/Sources/XCBuildSupport/XcodeBuildSystem.swift +++ b/Sources/XCBuildSupport/XcodeBuildSystem.swift @@ -90,7 +90,7 @@ package final class XcodeBuildSystem: SPMBuildCore.BuildSystem { self.fileSystem = fileSystem self.observabilityScope = observabilityScope.makeChildScope(description: "Xcode Build System") - if let xcbuildTool = ProcessEnv.vars["XCBUILD_TOOL"] { + if let xcbuildTool = ProcessEnv.block["XCBUILD_TOOL"] { xcbuildPath = try AbsolutePath(validating: xcbuildTool) } else { let xcodeSelectOutput = try TSCBasic.Process.popen(args: "xcode-select", "-p").utf8Output().spm_chomp() diff --git a/Tests/CommandsTests/APIDiffTests.swift b/Tests/CommandsTests/APIDiffTests.swift index fe934bcb4e9..602c36c532c 100644 --- a/Tests/CommandsTests/APIDiffTests.swift +++ b/Tests/CommandsTests/APIDiffTests.swift @@ -42,7 +42,7 @@ final class APIDiffTests: CommandsTestCase { try skipIfApiDigesterUnsupported() // The following is added to separate out the integration point testing of the API // diff digester with SwiftPM from the functionality tests of the digester itself - guard ProcessEnv.vars["SWIFTPM_TEST_API_DIFF_OUTPUT"] == "1" else { + guard ProcessEnv.block["SWIFTPM_TEST_API_DIFF_OUTPUT"] == "1" else { throw XCTSkip("Env var SWIFTPM_TEST_API_DIFF_OUTPUT must be set to test the output") } } diff --git a/Tests/PackageCollectionsTests/GitHubPackageMetadataProviderTests.swift b/Tests/PackageCollectionsTests/GitHubPackageMetadataProviderTests.swift index 26fc46b16d9..5de852f11ca 100644 --- a/Tests/PackageCollectionsTests/GitHubPackageMetadataProviderTests.swift +++ b/Tests/PackageCollectionsTests/GitHubPackageMetadataProviderTests.swift @@ -339,7 +339,7 @@ class GitHubPackageMetadataProviderTests: XCTestCase { httpClient.configuration.requestHeaders = .init() httpClient.configuration.requestHeaders!.add(name: "Cache-Control", value: "no-cache") var configuration = GitHubPackageMetadataProvider.Configuration(disableCache: true) // Disable cache so we hit the API - if let token = ProcessEnv.vars["GITHUB_API_TOKEN"] { + if let token = ProcessEnv.block["GITHUB_API_TOKEN"] { configuration.authTokens = { [.github("github.com"): token] } } configuration.apiLimitWarningThreshold = 50 From 259ec789da64751ec69739984eddb85b458fc407 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 5 Mar 2024 19:34:01 +0000 Subject: [PATCH 043/159] Remove previously deprecated `--enable-test-discovery` (#7391) This flag was deprecated for 4 years since Swift 5.4, Swift 6.0 is a good opportunity to remove it. Closes https://github.com/apple/swift-package-manager/issues/7389. --- Sources/Build/BuildPlan/BuildPlan+Test.swift | 78 +++++++------------ .../Commands/Utilities/TestingSupport.swift | 8 +- Sources/CoreCommands/Options.swift | 5 -- Sources/CoreCommands/SwiftCommandState.swift | 11 --- .../BuildParameters+Testing.swift | 16 +--- Tests/CommandsTests/TestCommandTests.swift | 30 ------- .../FunctionalTests/TestDiscoveryTests.swift | 15 ---- 7 files changed, 34 insertions(+), 129 deletions(-) diff --git a/Sources/Build/BuildPlan/BuildPlan+Test.swift b/Sources/Build/BuildPlan/BuildPlan+Test.swift index 58001c2816f..384b349b832 100644 --- a/Sources/Build/BuildPlan/BuildPlan+Test.swift +++ b/Sources/Build/BuildPlan/BuildPlan+Test.swift @@ -33,7 +33,7 @@ extension BuildPlan { _ observabilityScope: ObservabilityScope ) throws -> [(product: ResolvedProduct, discoveryTargetBuildDescription: SwiftTargetBuildDescription?, entryPointTargetBuildDescription: SwiftTargetBuildDescription)] { guard buildParameters.testingParameters.testProductStyle.requiresAdditionalDerivedTestTargets, - case .entryPointExecutable(let explicitlyEnabledDiscovery, let explicitlySpecifiedPath) = + case .entryPointExecutable(let explicitlySpecifiedPath) = buildParameters.testingParameters.testProductStyle else { throw InternalError("makeTestManifestTargets should not be used for build plan which does not require additional derived test targets") @@ -41,13 +41,11 @@ extension BuildPlan { let isEntryPointPathSpecifiedExplicitly = explicitlySpecifiedPath != nil - var isDiscoveryEnabledRedundantly = explicitlyEnabledDiscovery && !isEntryPointPathSpecifiedExplicitly var result: [(ResolvedProduct, SwiftTargetBuildDescription?, SwiftTargetBuildDescription)] = [] for testProduct in graph.allProducts where testProduct.type == .test { guard let package = graph.package(for: testProduct) else { throw InternalError("package not found for \(testProduct)") } - isDiscoveryEnabledRedundantly = isDiscoveryEnabledRedundantly && nil == testProduct.testEntryPointTarget // If a non-explicitly specified test entry point file exists, prefer that over test discovery. // This is designed as an escape hatch when test discovery is not appropriate and for backwards // compatibility for projects that have existing test entry point files (e.g. XCTMain.swift, LinuxMain.swift). @@ -58,10 +56,7 @@ extension BuildPlan { // `--experimental-test-entry-point-path `. The latter is useful because it still performs test discovery and places the discovered // tests into a separate target/module named "PackageDiscoveredTests". Then, that entry point file may import that module and // obtain that list to pass it to the `XCTMain(...)` function and avoid needing to maintain a list of tests itself. - if testProduct.testEntryPointTarget != nil && explicitlyEnabledDiscovery && !isEntryPointPathSpecifiedExplicitly { - let testEntryPointName = testProduct.underlying.testEntryPointPath?.basename ?? SwiftTarget.defaultTestEntryPointName - observabilityScope.emit(warning: "'--enable-test-discovery' was specified so the '\(testEntryPointName)' entry point file for '\(testProduct.name)' will be ignored and an entry point will be generated automatically. To use test discovery with a custom entry point file, pass '--experimental-test-entry-point-path '.") - } else if testProduct.testEntryPointTarget == nil, let testEntryPointPath = explicitlySpecifiedPath, !fileSystem.exists(testEntryPointPath) { + if testProduct.testEntryPointTarget == nil, let testEntryPointPath = explicitlySpecifiedPath, !fileSystem.exists(testEntryPointPath) { observabilityScope.emit(error: "'--experimental-test-entry-point-path' was specified but the file '\(testEntryPointPath)' could not be found.") } @@ -160,43 +155,34 @@ extension BuildPlan { } if let entryPointResolvedTarget = testProduct.testEntryPointTarget { - if isEntryPointPathSpecifiedExplicitly || explicitlyEnabledDiscovery { - if isEntryPointPathSpecifiedExplicitly { - // Allow using the explicitly-specified test entry point target, but still perform test discovery and thus declare a dependency on the discovery targets. - let entryPointTarget = SwiftTarget( - name: entryPointResolvedTarget.underlying.name, - dependencies: entryPointResolvedTarget.underlying.dependencies + swiftTargetDependencies, - packageAccess: entryPointResolvedTarget.packageAccess, - testEntryPointSources: entryPointResolvedTarget.underlying.sources - ) - let entryPointResolvedTarget = ResolvedTarget( - packageIdentity: testProduct.packageIdentity, - underlying: entryPointTarget, - dependencies: entryPointResolvedTarget.dependencies + resolvedTargetDependencies, - defaultLocalization: testProduct.defaultLocalization, - supportedPlatforms: testProduct.supportedPlatforms, - platformVersionProvider: testProduct.platformVersionProvider - ) - let entryPointTargetBuildDescription = try SwiftTargetBuildDescription( - package: package, - target: entryPointResolvedTarget, - toolsVersion: toolsVersion, - buildParameters: buildParameters, - testTargetRole: .entryPoint(isSynthesized: false), - disableSandbox: disableSandbox, - fileSystem: fileSystem, - observabilityScope: observabilityScope - ) - - result.append((testProduct, discoveryTargets?.buildDescription, entryPointTargetBuildDescription)) - } else { - // Ignore test entry point and synthesize one, declaring a dependency on the test discovery targets created above. - let entryPointTargetBuildDescription = try generateSynthesizedEntryPointTarget( - swiftTargetDependencies: swiftTargetDependencies, - resolvedTargetDependencies: resolvedTargetDependencies - ) - result.append((testProduct, discoveryTargets?.buildDescription, entryPointTargetBuildDescription)) - } + if isEntryPointPathSpecifiedExplicitly { + // Allow using the explicitly-specified test entry point target, but still perform test discovery and thus declare a dependency on the discovery targets. + let entryPointTarget = SwiftTarget( + name: entryPointResolvedTarget.underlying.name, + dependencies: entryPointResolvedTarget.underlying.dependencies + swiftTargetDependencies, + packageAccess: entryPointResolvedTarget.packageAccess, + testEntryPointSources: entryPointResolvedTarget.underlying.sources + ) + let entryPointResolvedTarget = ResolvedTarget( + packageIdentity: testProduct.packageIdentity, + underlying: entryPointTarget, + dependencies: entryPointResolvedTarget.dependencies + resolvedTargetDependencies, + defaultLocalization: testProduct.defaultLocalization, + supportedPlatforms: testProduct.supportedPlatforms, + platformVersionProvider: testProduct.platformVersionProvider + ) + let entryPointTargetBuildDescription = try SwiftTargetBuildDescription( + package: package, + target: entryPointResolvedTarget, + toolsVersion: toolsVersion, + buildParameters: buildParameters, + testTargetRole: .entryPoint(isSynthesized: false), + disableSandbox: disableSandbox, + fileSystem: fileSystem, + observabilityScope: observabilityScope + ) + + result.append((testProduct, discoveryTargets?.buildDescription, entryPointTargetBuildDescription)) } else { // Use the test entry point as-is, without performing test discovery. let entryPointTargetBuildDescription = try SwiftTargetBuildDescription( @@ -221,10 +207,6 @@ extension BuildPlan { } } - if isDiscoveryEnabledRedundantly { - observabilityScope.emit(warning: "'--enable-test-discovery' option is deprecated; tests are automatically discovered on all platforms") - } - return result } } diff --git a/Sources/Commands/Utilities/TestingSupport.swift b/Sources/Commands/Utilities/TestingSupport.swift index 43f889fc309..c52154555f8 100644 --- a/Sources/Commands/Utilities/TestingSupport.swift +++ b/Sources/Commands/Utilities/TestingSupport.swift @@ -215,19 +215,13 @@ extension SwiftCommandState { ) throws -> BuildParameters { var parameters = try self.productsBuildParameters - var explicitlyEnabledDiscovery = false var explicitlySpecifiedPath: AbsolutePath? - if case let .entryPointExecutable( - explicitlyEnabledDiscoveryValue, - explicitlySpecifiedPathValue - ) = parameters.testingParameters.testProductStyle { - explicitlyEnabledDiscovery = explicitlyEnabledDiscoveryValue + if case let .entryPointExecutable(explicitlySpecifiedPathValue) = parameters.testingParameters.testProductStyle { explicitlySpecifiedPath = explicitlySpecifiedPathValue } parameters.testingParameters = .init( configuration: parameters.configuration, targetTriple: parameters.triple, - forceTestDiscovery: explicitlyEnabledDiscovery, testEntryPointPath: explicitlySpecifiedPath, library: library ) diff --git a/Sources/CoreCommands/Options.swift b/Sources/CoreCommands/Options.swift index bcb60853c23..cb0653fff41 100644 --- a/Sources/CoreCommands/Options.swift +++ b/Sources/CoreCommands/Options.swift @@ -464,13 +464,8 @@ package struct BuildOptions: ParsableArguments { #endif } - /// Whether to enable test discovery on platforms without Objective-C runtime. - @Flag(help: .hidden) - package var enableTestDiscovery: Bool = false - /// Path of test entry point file to use, instead of synthesizing one or using `XCTMain.swift` in the package (if /// present). - /// This implies `--enable-test-discovery` @Option( name: .customLong("experimental-test-entry-point-path"), help: .hidden diff --git a/Sources/CoreCommands/SwiftCommandState.swift b/Sources/CoreCommands/SwiftCommandState.swift index 38c190ec386..b0459e47c4c 100644 --- a/Sources/CoreCommands/SwiftCommandState.swift +++ b/Sources/CoreCommands/SwiftCommandState.swift @@ -371,16 +371,6 @@ package final class SwiftCommandState { observabilityScope.emit(.mutuallyExclusiveArgumentsError(arguments: ["--arch", "--triple"])) } - // --enable-test-discovery should never be called on darwin based platforms - #if canImport(Darwin) - if options.build.enableTestDiscovery { - observabilityScope - .emit( - warning: "'--enable-test-discovery' option is deprecated; tests are automatically discovered on all platforms" - ) - } - #endif - if options.caching.shouldDisableManifestCaching { observabilityScope .emit( @@ -777,7 +767,6 @@ package final class SwiftCommandState { testingParameters: .init( configuration: options.build.configuration, targetTriple: triple, - forceTestDiscovery: options.build.enableTestDiscovery, // backwards compatibility, remove with --enable-test-discovery testEntryPointPath: options.build.testEntryPointPath ) ) diff --git a/Sources/SPMBuildCore/BuildParameters/BuildParameters+Testing.swift b/Sources/SPMBuildCore/BuildParameters/BuildParameters+Testing.swift index bdfb66bb6b8..73351f97305 100644 --- a/Sources/SPMBuildCore/BuildParameters/BuildParameters+Testing.swift +++ b/Sources/SPMBuildCore/BuildParameters/BuildParameters+Testing.swift @@ -28,15 +28,9 @@ extension BuildParameters { /// `--experimental-test-entry-point-path ` option, that file is used, otherwise if an `XCTMain.swift` /// (formerly `LinuxMain.swift`) file is located in the package, it is used. /// - /// - Parameter explicitlyEnabledDiscovery: Whether test discovery generation was forced by passing - /// `--enable-test-discovery`, overriding any custom test entry point file specified via other CLI options or located in - /// the package. /// - Parameter explicitlySpecifiedPath: The path to the test entry point file, if one was specified explicitly via /// `--experimental-test-entry-point-path `. - case entryPointExecutable( - explicitlyEnabledDiscovery: Bool, - explicitlySpecifiedPath: AbsolutePath? - ) + case entryPointExecutable(explicitlySpecifiedPath: AbsolutePath?) /// Whether this test product style requires additional, derived test targets, i.e. there must be additional test targets, beyond those /// listed explicitly in the package manifest, created in order to add additional behavior (such as entry point logic). @@ -54,7 +48,7 @@ extension BuildParameters { switch self { case .loadableBundle: return nil - case .entryPointExecutable(explicitlyEnabledDiscovery: _, explicitlySpecifiedPath: let entryPointPath): + case .entryPointExecutable(explicitlySpecifiedPath: let entryPointPath): return entryPointPath } } @@ -66,7 +60,6 @@ extension BuildParameters { public enum CodingKeys: CodingKey { case _case - case explicitlyEnabledDiscovery case explicitlySpecifiedPath } @@ -75,9 +68,8 @@ extension BuildParameters { switch self { case .loadableBundle: try container.encode(DiscriminatorKeys.loadableBundle, forKey: ._case) - case .entryPointExecutable(let explicitlyEnabledDiscovery, let explicitlySpecifiedPath): + case .entryPointExecutable(let explicitlySpecifiedPath): try container.encode(DiscriminatorKeys.entryPointExecutable, forKey: ._case) - try container.encode(explicitlyEnabledDiscovery, forKey: .explicitlyEnabledDiscovery) try container.encode(explicitlySpecifiedPath, forKey: .explicitlySpecifiedPath) } } @@ -122,7 +114,6 @@ extension BuildParameters { enableCodeCoverage: Bool = false, enableTestability: Bool? = nil, experimentalTestOutput: Bool = false, - forceTestDiscovery: Bool = false, testEntryPointPath: AbsolutePath? = nil, library: Library = .xctest ) { @@ -137,7 +128,6 @@ extension BuildParameters { // to disable testability in `swift test`, but that requires that the tests do not use the testable imports feature self.enableTestability = enableTestability ?? (.debug == configuration) self.testProductStyle = (targetTriple.isDarwin() && library == .xctest) ? .loadableBundle : .entryPointExecutable( - explicitlyEnabledDiscovery: forceTestDiscovery, explicitlySpecifiedPath: testEntryPointPath ) self.library = library diff --git a/Tests/CommandsTests/TestCommandTests.swift b/Tests/CommandsTests/TestCommandTests.swift index 2f485ded9a4..4c76b715b67 100644 --- a/Tests/CommandsTests/TestCommandTests.swift +++ b/Tests/CommandsTests/TestCommandTests.swift @@ -202,36 +202,6 @@ final class TestCommandTests: CommandsTestCase { } } - func testEnableTestDiscoveryDeprecation() throws { - let compilerDiagnosticFlags = ["-Xswiftc", "-Xfrontend", "-Xswiftc", "-Rmodule-interface-rebuild"] - #if canImport(Darwin) - // should emit when LinuxMain is present - try fixture(name: "Miscellaneous/TestDiscovery/Simple") { fixturePath in - let (_, stderr) = try SwiftPM.Test.execute(["--enable-test-discovery"] + compilerDiagnosticFlags, packagePath: fixturePath) - XCTAssertMatch(stderr, .contains("warning: '--enable-test-discovery' option is deprecated")) - } - - // should emit when LinuxMain is not present - try fixture(name: "Miscellaneous/TestDiscovery/Simple") { fixturePath in - try localFileSystem.writeFileContents(fixturePath.appending(components: "Tests", SwiftTarget.defaultTestEntryPointName), bytes: "fatalError(\"boom\")") - let (_, stderr) = try SwiftPM.Test.execute(["--enable-test-discovery"] + compilerDiagnosticFlags, packagePath: fixturePath) - XCTAssertMatch(stderr, .contains("warning: '--enable-test-discovery' option is deprecated")) - } - #else - // should emit when LinuxMain is present - try fixture(name: "Miscellaneous/TestDiscovery/Simple") { fixturePath in - let (_, stderr) = try SwiftPM.Test.execute(["--enable-test-discovery"] + compilerDiagnosticFlags, packagePath: fixturePath) - XCTAssertMatch(stderr, .contains("warning: '--enable-test-discovery' option is deprecated")) - } - // should not emit when LinuxMain is present - try fixture(name: "Miscellaneous/TestDiscovery/Simple") { fixturePath in - try localFileSystem.writeFileContents(fixturePath.appending(components: "Tests", SwiftTarget.defaultTestEntryPointName), bytes: "fatalError(\"boom\")") - let (_, stderr) = try SwiftPM.Test.execute(["--enable-test-discovery"] + compilerDiagnosticFlags, packagePath: fixturePath) - XCTAssertNoMatch(stderr, .contains("warning: '--enable-test-discovery' option is deprecated")) - } - #endif - } - func testList() throws { try fixture(name: "Miscellaneous/TestDiscovery/Simple") { fixturePath in let (stdout, stderr) = try SwiftPM.Test.execute(["list"], packagePath: fixturePath) diff --git a/Tests/FunctionalTests/TestDiscoveryTests.swift b/Tests/FunctionalTests/TestDiscoveryTests.swift index 05d14fe5edd..45f08fdefae 100644 --- a/Tests/FunctionalTests/TestDiscoveryTests.swift +++ b/Tests/FunctionalTests/TestDiscoveryTests.swift @@ -88,21 +88,6 @@ class TestDiscoveryTests: XCTestCase { } } - func testEntryPointOverrideIgnored() throws { - #if os(macOS) - try XCTSkipIf(true) - #endif - try fixture(name: "Miscellaneous/TestDiscovery/Simple") { fixturePath in - let manifestPath = fixturePath.appending(components: "Tests", SwiftTarget.defaultTestEntryPointName) - try localFileSystem.writeFileContents(manifestPath, string: "fatalError(\"should not be called\")") - let (stdout, stderr) = try executeSwiftTest(fixturePath, extraArgs: ["--enable-test-discovery"]) - // in "swift test" build output goes to stderr - XCTAssertMatch(stderr, .contains("Build complete!")) - // in "swift test" test output goes to stdout - XCTAssertNoMatch(stdout, .contains("Executed 1 test")) - } - } - func testTestExtensions() throws { #if os(macOS) try XCTSkipIf(true) From a5f9b6cf7ceeea13b7db828b5eece2ca9e0df445 Mon Sep 17 00:00:00 2001 From: Ben Barham Date: Tue, 5 Mar 2024 15:52:19 -0800 Subject: [PATCH 044/159] Revert "Remove previously deprecated `--enable-test-discovery`" (#7395) Reverts apple/swift-package-manager#7391, which blocks CI as various projects are still using `--enable-test-discovery`. --- Sources/Build/BuildPlan/BuildPlan+Test.swift | 78 ++++++++++++------- .../Commands/Utilities/TestingSupport.swift | 8 +- Sources/CoreCommands/Options.swift | 5 ++ Sources/CoreCommands/SwiftCommandState.swift | 11 +++ .../BuildParameters+Testing.swift | 16 +++- Tests/CommandsTests/TestCommandTests.swift | 30 +++++++ .../FunctionalTests/TestDiscoveryTests.swift | 15 ++++ 7 files changed, 129 insertions(+), 34 deletions(-) diff --git a/Sources/Build/BuildPlan/BuildPlan+Test.swift b/Sources/Build/BuildPlan/BuildPlan+Test.swift index 384b349b832..58001c2816f 100644 --- a/Sources/Build/BuildPlan/BuildPlan+Test.swift +++ b/Sources/Build/BuildPlan/BuildPlan+Test.swift @@ -33,7 +33,7 @@ extension BuildPlan { _ observabilityScope: ObservabilityScope ) throws -> [(product: ResolvedProduct, discoveryTargetBuildDescription: SwiftTargetBuildDescription?, entryPointTargetBuildDescription: SwiftTargetBuildDescription)] { guard buildParameters.testingParameters.testProductStyle.requiresAdditionalDerivedTestTargets, - case .entryPointExecutable(let explicitlySpecifiedPath) = + case .entryPointExecutable(let explicitlyEnabledDiscovery, let explicitlySpecifiedPath) = buildParameters.testingParameters.testProductStyle else { throw InternalError("makeTestManifestTargets should not be used for build plan which does not require additional derived test targets") @@ -41,11 +41,13 @@ extension BuildPlan { let isEntryPointPathSpecifiedExplicitly = explicitlySpecifiedPath != nil + var isDiscoveryEnabledRedundantly = explicitlyEnabledDiscovery && !isEntryPointPathSpecifiedExplicitly var result: [(ResolvedProduct, SwiftTargetBuildDescription?, SwiftTargetBuildDescription)] = [] for testProduct in graph.allProducts where testProduct.type == .test { guard let package = graph.package(for: testProduct) else { throw InternalError("package not found for \(testProduct)") } + isDiscoveryEnabledRedundantly = isDiscoveryEnabledRedundantly && nil == testProduct.testEntryPointTarget // If a non-explicitly specified test entry point file exists, prefer that over test discovery. // This is designed as an escape hatch when test discovery is not appropriate and for backwards // compatibility for projects that have existing test entry point files (e.g. XCTMain.swift, LinuxMain.swift). @@ -56,7 +58,10 @@ extension BuildPlan { // `--experimental-test-entry-point-path `. The latter is useful because it still performs test discovery and places the discovered // tests into a separate target/module named "PackageDiscoveredTests". Then, that entry point file may import that module and // obtain that list to pass it to the `XCTMain(...)` function and avoid needing to maintain a list of tests itself. - if testProduct.testEntryPointTarget == nil, let testEntryPointPath = explicitlySpecifiedPath, !fileSystem.exists(testEntryPointPath) { + if testProduct.testEntryPointTarget != nil && explicitlyEnabledDiscovery && !isEntryPointPathSpecifiedExplicitly { + let testEntryPointName = testProduct.underlying.testEntryPointPath?.basename ?? SwiftTarget.defaultTestEntryPointName + observabilityScope.emit(warning: "'--enable-test-discovery' was specified so the '\(testEntryPointName)' entry point file for '\(testProduct.name)' will be ignored and an entry point will be generated automatically. To use test discovery with a custom entry point file, pass '--experimental-test-entry-point-path '.") + } else if testProduct.testEntryPointTarget == nil, let testEntryPointPath = explicitlySpecifiedPath, !fileSystem.exists(testEntryPointPath) { observabilityScope.emit(error: "'--experimental-test-entry-point-path' was specified but the file '\(testEntryPointPath)' could not be found.") } @@ -155,34 +160,43 @@ extension BuildPlan { } if let entryPointResolvedTarget = testProduct.testEntryPointTarget { - if isEntryPointPathSpecifiedExplicitly { - // Allow using the explicitly-specified test entry point target, but still perform test discovery and thus declare a dependency on the discovery targets. - let entryPointTarget = SwiftTarget( - name: entryPointResolvedTarget.underlying.name, - dependencies: entryPointResolvedTarget.underlying.dependencies + swiftTargetDependencies, - packageAccess: entryPointResolvedTarget.packageAccess, - testEntryPointSources: entryPointResolvedTarget.underlying.sources - ) - let entryPointResolvedTarget = ResolvedTarget( - packageIdentity: testProduct.packageIdentity, - underlying: entryPointTarget, - dependencies: entryPointResolvedTarget.dependencies + resolvedTargetDependencies, - defaultLocalization: testProduct.defaultLocalization, - supportedPlatforms: testProduct.supportedPlatforms, - platformVersionProvider: testProduct.platformVersionProvider - ) - let entryPointTargetBuildDescription = try SwiftTargetBuildDescription( - package: package, - target: entryPointResolvedTarget, - toolsVersion: toolsVersion, - buildParameters: buildParameters, - testTargetRole: .entryPoint(isSynthesized: false), - disableSandbox: disableSandbox, - fileSystem: fileSystem, - observabilityScope: observabilityScope - ) - - result.append((testProduct, discoveryTargets?.buildDescription, entryPointTargetBuildDescription)) + if isEntryPointPathSpecifiedExplicitly || explicitlyEnabledDiscovery { + if isEntryPointPathSpecifiedExplicitly { + // Allow using the explicitly-specified test entry point target, but still perform test discovery and thus declare a dependency on the discovery targets. + let entryPointTarget = SwiftTarget( + name: entryPointResolvedTarget.underlying.name, + dependencies: entryPointResolvedTarget.underlying.dependencies + swiftTargetDependencies, + packageAccess: entryPointResolvedTarget.packageAccess, + testEntryPointSources: entryPointResolvedTarget.underlying.sources + ) + let entryPointResolvedTarget = ResolvedTarget( + packageIdentity: testProduct.packageIdentity, + underlying: entryPointTarget, + dependencies: entryPointResolvedTarget.dependencies + resolvedTargetDependencies, + defaultLocalization: testProduct.defaultLocalization, + supportedPlatforms: testProduct.supportedPlatforms, + platformVersionProvider: testProduct.platformVersionProvider + ) + let entryPointTargetBuildDescription = try SwiftTargetBuildDescription( + package: package, + target: entryPointResolvedTarget, + toolsVersion: toolsVersion, + buildParameters: buildParameters, + testTargetRole: .entryPoint(isSynthesized: false), + disableSandbox: disableSandbox, + fileSystem: fileSystem, + observabilityScope: observabilityScope + ) + + result.append((testProduct, discoveryTargets?.buildDescription, entryPointTargetBuildDescription)) + } else { + // Ignore test entry point and synthesize one, declaring a dependency on the test discovery targets created above. + let entryPointTargetBuildDescription = try generateSynthesizedEntryPointTarget( + swiftTargetDependencies: swiftTargetDependencies, + resolvedTargetDependencies: resolvedTargetDependencies + ) + result.append((testProduct, discoveryTargets?.buildDescription, entryPointTargetBuildDescription)) + } } else { // Use the test entry point as-is, without performing test discovery. let entryPointTargetBuildDescription = try SwiftTargetBuildDescription( @@ -207,6 +221,10 @@ extension BuildPlan { } } + if isDiscoveryEnabledRedundantly { + observabilityScope.emit(warning: "'--enable-test-discovery' option is deprecated; tests are automatically discovered on all platforms") + } + return result } } diff --git a/Sources/Commands/Utilities/TestingSupport.swift b/Sources/Commands/Utilities/TestingSupport.swift index c52154555f8..43f889fc309 100644 --- a/Sources/Commands/Utilities/TestingSupport.swift +++ b/Sources/Commands/Utilities/TestingSupport.swift @@ -215,13 +215,19 @@ extension SwiftCommandState { ) throws -> BuildParameters { var parameters = try self.productsBuildParameters + var explicitlyEnabledDiscovery = false var explicitlySpecifiedPath: AbsolutePath? - if case let .entryPointExecutable(explicitlySpecifiedPathValue) = parameters.testingParameters.testProductStyle { + if case let .entryPointExecutable( + explicitlyEnabledDiscoveryValue, + explicitlySpecifiedPathValue + ) = parameters.testingParameters.testProductStyle { + explicitlyEnabledDiscovery = explicitlyEnabledDiscoveryValue explicitlySpecifiedPath = explicitlySpecifiedPathValue } parameters.testingParameters = .init( configuration: parameters.configuration, targetTriple: parameters.triple, + forceTestDiscovery: explicitlyEnabledDiscovery, testEntryPointPath: explicitlySpecifiedPath, library: library ) diff --git a/Sources/CoreCommands/Options.swift b/Sources/CoreCommands/Options.swift index cb0653fff41..bcb60853c23 100644 --- a/Sources/CoreCommands/Options.swift +++ b/Sources/CoreCommands/Options.swift @@ -464,8 +464,13 @@ package struct BuildOptions: ParsableArguments { #endif } + /// Whether to enable test discovery on platforms without Objective-C runtime. + @Flag(help: .hidden) + package var enableTestDiscovery: Bool = false + /// Path of test entry point file to use, instead of synthesizing one or using `XCTMain.swift` in the package (if /// present). + /// This implies `--enable-test-discovery` @Option( name: .customLong("experimental-test-entry-point-path"), help: .hidden diff --git a/Sources/CoreCommands/SwiftCommandState.swift b/Sources/CoreCommands/SwiftCommandState.swift index b0459e47c4c..38c190ec386 100644 --- a/Sources/CoreCommands/SwiftCommandState.swift +++ b/Sources/CoreCommands/SwiftCommandState.swift @@ -371,6 +371,16 @@ package final class SwiftCommandState { observabilityScope.emit(.mutuallyExclusiveArgumentsError(arguments: ["--arch", "--triple"])) } + // --enable-test-discovery should never be called on darwin based platforms + #if canImport(Darwin) + if options.build.enableTestDiscovery { + observabilityScope + .emit( + warning: "'--enable-test-discovery' option is deprecated; tests are automatically discovered on all platforms" + ) + } + #endif + if options.caching.shouldDisableManifestCaching { observabilityScope .emit( @@ -767,6 +777,7 @@ package final class SwiftCommandState { testingParameters: .init( configuration: options.build.configuration, targetTriple: triple, + forceTestDiscovery: options.build.enableTestDiscovery, // backwards compatibility, remove with --enable-test-discovery testEntryPointPath: options.build.testEntryPointPath ) ) diff --git a/Sources/SPMBuildCore/BuildParameters/BuildParameters+Testing.swift b/Sources/SPMBuildCore/BuildParameters/BuildParameters+Testing.swift index 73351f97305..bdfb66bb6b8 100644 --- a/Sources/SPMBuildCore/BuildParameters/BuildParameters+Testing.swift +++ b/Sources/SPMBuildCore/BuildParameters/BuildParameters+Testing.swift @@ -28,9 +28,15 @@ extension BuildParameters { /// `--experimental-test-entry-point-path ` option, that file is used, otherwise if an `XCTMain.swift` /// (formerly `LinuxMain.swift`) file is located in the package, it is used. /// + /// - Parameter explicitlyEnabledDiscovery: Whether test discovery generation was forced by passing + /// `--enable-test-discovery`, overriding any custom test entry point file specified via other CLI options or located in + /// the package. /// - Parameter explicitlySpecifiedPath: The path to the test entry point file, if one was specified explicitly via /// `--experimental-test-entry-point-path `. - case entryPointExecutable(explicitlySpecifiedPath: AbsolutePath?) + case entryPointExecutable( + explicitlyEnabledDiscovery: Bool, + explicitlySpecifiedPath: AbsolutePath? + ) /// Whether this test product style requires additional, derived test targets, i.e. there must be additional test targets, beyond those /// listed explicitly in the package manifest, created in order to add additional behavior (such as entry point logic). @@ -48,7 +54,7 @@ extension BuildParameters { switch self { case .loadableBundle: return nil - case .entryPointExecutable(explicitlySpecifiedPath: let entryPointPath): + case .entryPointExecutable(explicitlyEnabledDiscovery: _, explicitlySpecifiedPath: let entryPointPath): return entryPointPath } } @@ -60,6 +66,7 @@ extension BuildParameters { public enum CodingKeys: CodingKey { case _case + case explicitlyEnabledDiscovery case explicitlySpecifiedPath } @@ -68,8 +75,9 @@ extension BuildParameters { switch self { case .loadableBundle: try container.encode(DiscriminatorKeys.loadableBundle, forKey: ._case) - case .entryPointExecutable(let explicitlySpecifiedPath): + case .entryPointExecutable(let explicitlyEnabledDiscovery, let explicitlySpecifiedPath): try container.encode(DiscriminatorKeys.entryPointExecutable, forKey: ._case) + try container.encode(explicitlyEnabledDiscovery, forKey: .explicitlyEnabledDiscovery) try container.encode(explicitlySpecifiedPath, forKey: .explicitlySpecifiedPath) } } @@ -114,6 +122,7 @@ extension BuildParameters { enableCodeCoverage: Bool = false, enableTestability: Bool? = nil, experimentalTestOutput: Bool = false, + forceTestDiscovery: Bool = false, testEntryPointPath: AbsolutePath? = nil, library: Library = .xctest ) { @@ -128,6 +137,7 @@ extension BuildParameters { // to disable testability in `swift test`, but that requires that the tests do not use the testable imports feature self.enableTestability = enableTestability ?? (.debug == configuration) self.testProductStyle = (targetTriple.isDarwin() && library == .xctest) ? .loadableBundle : .entryPointExecutable( + explicitlyEnabledDiscovery: forceTestDiscovery, explicitlySpecifiedPath: testEntryPointPath ) self.library = library diff --git a/Tests/CommandsTests/TestCommandTests.swift b/Tests/CommandsTests/TestCommandTests.swift index 4c76b715b67..2f485ded9a4 100644 --- a/Tests/CommandsTests/TestCommandTests.swift +++ b/Tests/CommandsTests/TestCommandTests.swift @@ -202,6 +202,36 @@ final class TestCommandTests: CommandsTestCase { } } + func testEnableTestDiscoveryDeprecation() throws { + let compilerDiagnosticFlags = ["-Xswiftc", "-Xfrontend", "-Xswiftc", "-Rmodule-interface-rebuild"] + #if canImport(Darwin) + // should emit when LinuxMain is present + try fixture(name: "Miscellaneous/TestDiscovery/Simple") { fixturePath in + let (_, stderr) = try SwiftPM.Test.execute(["--enable-test-discovery"] + compilerDiagnosticFlags, packagePath: fixturePath) + XCTAssertMatch(stderr, .contains("warning: '--enable-test-discovery' option is deprecated")) + } + + // should emit when LinuxMain is not present + try fixture(name: "Miscellaneous/TestDiscovery/Simple") { fixturePath in + try localFileSystem.writeFileContents(fixturePath.appending(components: "Tests", SwiftTarget.defaultTestEntryPointName), bytes: "fatalError(\"boom\")") + let (_, stderr) = try SwiftPM.Test.execute(["--enable-test-discovery"] + compilerDiagnosticFlags, packagePath: fixturePath) + XCTAssertMatch(stderr, .contains("warning: '--enable-test-discovery' option is deprecated")) + } + #else + // should emit when LinuxMain is present + try fixture(name: "Miscellaneous/TestDiscovery/Simple") { fixturePath in + let (_, stderr) = try SwiftPM.Test.execute(["--enable-test-discovery"] + compilerDiagnosticFlags, packagePath: fixturePath) + XCTAssertMatch(stderr, .contains("warning: '--enable-test-discovery' option is deprecated")) + } + // should not emit when LinuxMain is present + try fixture(name: "Miscellaneous/TestDiscovery/Simple") { fixturePath in + try localFileSystem.writeFileContents(fixturePath.appending(components: "Tests", SwiftTarget.defaultTestEntryPointName), bytes: "fatalError(\"boom\")") + let (_, stderr) = try SwiftPM.Test.execute(["--enable-test-discovery"] + compilerDiagnosticFlags, packagePath: fixturePath) + XCTAssertNoMatch(stderr, .contains("warning: '--enable-test-discovery' option is deprecated")) + } + #endif + } + func testList() throws { try fixture(name: "Miscellaneous/TestDiscovery/Simple") { fixturePath in let (stdout, stderr) = try SwiftPM.Test.execute(["list"], packagePath: fixturePath) diff --git a/Tests/FunctionalTests/TestDiscoveryTests.swift b/Tests/FunctionalTests/TestDiscoveryTests.swift index 45f08fdefae..05d14fe5edd 100644 --- a/Tests/FunctionalTests/TestDiscoveryTests.swift +++ b/Tests/FunctionalTests/TestDiscoveryTests.swift @@ -88,6 +88,21 @@ class TestDiscoveryTests: XCTestCase { } } + func testEntryPointOverrideIgnored() throws { + #if os(macOS) + try XCTSkipIf(true) + #endif + try fixture(name: "Miscellaneous/TestDiscovery/Simple") { fixturePath in + let manifestPath = fixturePath.appending(components: "Tests", SwiftTarget.defaultTestEntryPointName) + try localFileSystem.writeFileContents(manifestPath, string: "fatalError(\"should not be called\")") + let (stdout, stderr) = try executeSwiftTest(fixturePath, extraArgs: ["--enable-test-discovery"]) + // in "swift test" build output goes to stderr + XCTAssertMatch(stderr, .contains("Build complete!")) + // in "swift test" test output goes to stdout + XCTAssertNoMatch(stdout, .contains("Executed 1 test")) + } + } + func testTestExtensions() throws { #if os(macOS) try XCTSkipIf(true) From b49a22787a5873f8f67e5de48ae3ba7499bfb9d8 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Wed, 6 Mar 2024 13:51:57 -0500 Subject: [PATCH 045/159] Pass all arguments when initing testing parameters in `swift build` (#7396) Ensures that a fully-initialized `TestingParameters` structure is used with `swift build --build-tests` instead of using any default parameters (which will tend us toward the wrong output with packages that use `--enable-test-discovery` and still have LinuxMain.swift files.) Tested with swift-numerics on Ubuntu 22.04 aarch64; before the change, we'd hit the `fatalError()` call in that package's LinuxMain.swift file. After the change, we correctly run XCTest-based tests. Resolves #7389. --- .../IgnoresLinuxMain/Package.swift | 9 +++++++ .../IgnoresLinuxMainTests/SomeTest.swift | 5 ++++ .../IgnoresLinuxMain/Tests/LinuxMain.swift | 1 + Sources/Commands/SwiftBuildCommand.swift | 5 ++++ Tests/CommandsTests/BuildCommandTests.swift | 24 +++++++++++++++++-- 5 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 Fixtures/Miscellaneous/TestDiscovery/IgnoresLinuxMain/Package.swift create mode 100644 Fixtures/Miscellaneous/TestDiscovery/IgnoresLinuxMain/Tests/IgnoresLinuxMainTests/SomeTest.swift create mode 100644 Fixtures/Miscellaneous/TestDiscovery/IgnoresLinuxMain/Tests/LinuxMain.swift diff --git a/Fixtures/Miscellaneous/TestDiscovery/IgnoresLinuxMain/Package.swift b/Fixtures/Miscellaneous/TestDiscovery/IgnoresLinuxMain/Package.swift new file mode 100644 index 00000000000..d7749959468 --- /dev/null +++ b/Fixtures/Miscellaneous/TestDiscovery/IgnoresLinuxMain/Package.swift @@ -0,0 +1,9 @@ +// swift-tools-version:5.10 +import PackageDescription + +let package = Package( + name: "IgnoresLinuxMain", + targets: [ + .testTarget(name: "IgnoresLinuxMainTests"), + ] +) diff --git a/Fixtures/Miscellaneous/TestDiscovery/IgnoresLinuxMain/Tests/IgnoresLinuxMainTests/SomeTest.swift b/Fixtures/Miscellaneous/TestDiscovery/IgnoresLinuxMain/Tests/IgnoresLinuxMainTests/SomeTest.swift new file mode 100644 index 00000000000..dec36ed294c --- /dev/null +++ b/Fixtures/Miscellaneous/TestDiscovery/IgnoresLinuxMain/Tests/IgnoresLinuxMainTests/SomeTest.swift @@ -0,0 +1,5 @@ +import XCTest + +final class SomeTests: XCTestCase { + func testSomething() {} +} diff --git a/Fixtures/Miscellaneous/TestDiscovery/IgnoresLinuxMain/Tests/LinuxMain.swift b/Fixtures/Miscellaneous/TestDiscovery/IgnoresLinuxMain/Tests/LinuxMain.swift new file mode 100644 index 00000000000..f7b651fc7aa --- /dev/null +++ b/Fixtures/Miscellaneous/TestDiscovery/IgnoresLinuxMain/Tests/LinuxMain.swift @@ -0,0 +1 @@ +fatalError("Should not use the contents of LinuxMain.swift") diff --git a/Sources/Commands/SwiftBuildCommand.swift b/Sources/Commands/SwiftBuildCommand.swift index e55f533dcce..6d74dd63c25 100644 --- a/Sources/Commands/SwiftBuildCommand.swift +++ b/Sources/Commands/SwiftBuildCommand.swift @@ -154,6 +154,11 @@ package struct SwiftBuildCommand: AsyncSwiftCommand { buildParameters.testingParameters = .init( configuration: buildParameters.configuration, targetTriple: buildParameters.triple, + enableCodeCoverage: buildParameters.testingParameters.enableCodeCoverage, + enableTestability: buildParameters.testingParameters.enableTestability, + experimentalTestOutput: buildParameters.testingParameters.experimentalTestOutput, + forceTestDiscovery: globalOptions.build.enableTestDiscovery, + testEntryPointPath: globalOptions.build.testEntryPointPath, library: library ) try build(swiftCommandState, subset: subset, buildParameters: buildParameters) diff --git a/Tests/CommandsTests/BuildCommandTests.swift b/Tests/CommandsTests/BuildCommandTests.swift index b54c6726dbb..ea54f5a12da 100644 --- a/Tests/CommandsTests/BuildCommandTests.swift +++ b/Tests/CommandsTests/BuildCommandTests.swift @@ -42,10 +42,14 @@ final class BuildCommandTests: CommandsTestCase { try SwiftPM.Build.execute(args, packagePath: packagePath, env: environment) } - func build(_ args: [String], packagePath: AbsolutePath? = nil, isRelease: Bool = false) throws -> BuildResult { + func build(_ args: [String], packagePath: AbsolutePath? = nil, isRelease: Bool = false, cleanAfterward: Bool = true) throws -> BuildResult { let buildConfigurationArguments = isRelease ? ["-c", "release"] : [] let (stdout, stderr) = try execute(args + buildConfigurationArguments, packagePath: packagePath) - defer { try! SwiftPM.Package.execute(["clean"], packagePath: packagePath) } + defer { + if cleanAfterward { + try! SwiftPM.Package.execute(["clean"], packagePath: packagePath) + } + } let (binPathOutput, _) = try execute( ["--show-bin-path"] + buildConfigurationArguments, packagePath: packagePath @@ -644,4 +648,20 @@ final class BuildCommandTests: CommandsTestCase { XCTAssertNoMatch(buildResult.stdout, .contains("codesign --force --sign - --entitlements")) } } + +#if !canImport(Darwin) + func testIgnoresLinuxMain() throws { + try fixture(name: "Miscellaneous/TestDiscovery/IgnoresLinuxMain") { fixturePath in + let buildResult = try self.build(["-v", "--build-tests", "--enable-test-discovery"], packagePath: fixturePath, cleanAfterward: false) + let testBinaryPath = buildResult.binPath.appending("IgnoresLinuxMainPackageTests.xctest") + + let processTerminated = expectation(description: "Process terminated") + _ = try Process.run(testBinaryPath.asURL, arguments: [] ) { process in + XCTAssertEqual(process.terminationStatus, EXIT_SUCCESS) + processTerminated.fulfill() + } + wait(for: [processTerminated], timeout: .infinity) + } + } +#endif } From 4e244463a349782869149f1158af8bea6c77d94e Mon Sep 17 00:00:00 2001 From: Nakaoka Rei Date: Wed, 13 Mar 2024 20:03:25 +0900 Subject: [PATCH 046/159] Fix comment wording in parameter description (#7401) I fixed a grammatical error in PackageDescription. Specifically, I changed the part that says `to let to let` to `to let`. --- Documentation/PackageDescription.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/PackageDescription.md b/Documentation/PackageDescription.md index 7d2b6aa1260..9595c9b41e1 100644 --- a/Documentation/PackageDescription.md +++ b/Documentation/PackageDescription.md @@ -235,7 +235,7 @@ let package = Package( /// - Parameters: /// - name: The name of the library product. /// - type: The optional type of the library that is used to determine how to link to the library. -/// Leave this parameter unspecified to let to let the Swift Package Manager choose between static or dynamic linking (recommended). +/// Leave this parameter unspecified to let the Swift Package Manager choose between static or dynamic linking (recommended). /// If you do not support both linkage types, use `.static` or `.dynamic` for this parameter. /// - targets: The targets that are bundled into a library product. static func library(name: String, type: Product.Library.LibraryType? = nil, targets: [String]) -> Product From 24cd4a25d206474b64a0956bd2f1b78d94f8f758 Mon Sep 17 00:00:00 2001 From: finagolfin Date: Thu, 14 Mar 2024 04:27:20 +0530 Subject: [PATCH 047/159] Replace getdtablesize() on Android (#7402) Works around the existing use of `getdtablesize()` on Android. --- Sources/Commands/SwiftRunCommand.swift | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Sources/Commands/SwiftRunCommand.swift b/Sources/Commands/SwiftRunCommand.swift index 217a5a2268c..98838358048 100644 --- a/Sources/Commands/SwiftRunCommand.swift +++ b/Sources/Commands/SwiftRunCommand.swift @@ -297,9 +297,15 @@ package struct SwiftRunCommand: AsyncSwiftCommand { var sig_set_all = sigset_t() sigfillset(&sig_set_all) sigprocmask(SIG_UNBLOCK, &sig_set_all, nil) + + #if os(Android) + let number_fds = Int32(sysconf(_SC_OPEN_MAX)) + #else + let number_fds = getdtablesize() + #endif // 2. close all file descriptors. - for i in 3.. Date: Thu, 14 Mar 2024 11:35:53 +0900 Subject: [PATCH 048/159] Fix XCTest entrypoint for WASI by making it async (#7400) --- ...BuildOperationBuildSystemDelegateHandler.swift | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Sources/Build/BuildOperationBuildSystemDelegateHandler.swift b/Sources/Build/BuildOperationBuildSystemDelegateHandler.swift index 2341eac5466..0a6559c9cab 100644 --- a/Sources/Build/BuildOperationBuildSystemDelegateHandler.swift +++ b/Sources/Build/BuildOperationBuildSystemDelegateHandler.swift @@ -264,17 +264,18 @@ final class TestEntryPointCommand: CustomLLBuildCommand, TestBuildCommand { @main @available(*, deprecated, message: "Not actually deprecated. Marked as deprecated to allow inclusion of deprecated tests (which test deprecated functionality) without warnings") struct Runner { + #if os(WASI) + /// On WASI, we can't block the main thread, so XCTestMain is defined as async. + static func main() async { + \#(testObservabilitySetup) + await XCTMain(__allDiscoveredTests()) as Never + } + #else static func main() { \#(testObservabilitySetup) - #if os(WASI) - // FIXME: On WASI, XCTest uses `Task` based waiting not to block the whole process, so - // the `XCTMain` call can return the control and the process will exit by `exit(0)` later. - // This is a workaround until we have WASI threads or swift-testing, which does not block threads. - XCTMain(__allDiscoveredTests()) - #else XCTMain(__allDiscoveredTests()) as Never - #endif } + #endif } """# ) From e73cb5b5efa1204024fa84e101dc57a665e5a3c8 Mon Sep 17 00:00:00 2001 From: Kishore Pusukuri Date: Tue, 19 Mar 2024 13:09:20 -0700 Subject: [PATCH 049/159] Suppress redundant linkage warnings due to dependency linkage propagation (#7404) Suppress redundant linkage warnings due to dependency linkage propagation rdar://112671586 ### Motivation: Improve user experience by suppressing redundant linkage warnings ### Modifications: Pass "-no_warn_duplicate_libraries" to linker. ### Result: We should not see warnings such as "ld: warning: ignoring duplicate libraries: '-lsqlite3'" --------- Co-authored-by: Max Desiatov --- .../DoNotFilterLinkerDiagnostics/Package.swift | 4 +--- .../ProductBuildDescription.swift | 6 ++++++ Sources/XCBuildSupport/PIFBuilder.swift | 3 +++ Tests/BuildTests/BuildPlanTests.swift | 15 +++++++++++++++ Tests/BuildTests/BuildSystemDelegateTests.swift | 2 +- Tests/XCBuildSupportTests/PIFBuilderTests.swift | 6 ++++-- 6 files changed, 30 insertions(+), 6 deletions(-) diff --git a/Fixtures/Miscellaneous/DoNotFilterLinkerDiagnostics/Package.swift b/Fixtures/Miscellaneous/DoNotFilterLinkerDiagnostics/Package.swift index 45efb831fa1..636e93fcc67 100644 --- a/Fixtures/Miscellaneous/DoNotFilterLinkerDiagnostics/Package.swift +++ b/Fixtures/Miscellaneous/DoNotFilterLinkerDiagnostics/Package.swift @@ -8,9 +8,7 @@ let package = Package( .executableTarget( name: "DoNotFilterLinkerDiagnostics", linkerSettings: [ - .linkedLibrary("z"), - .unsafeFlags(["-lz"]), - // should produce: ld: warning: ignoring duplicate libraries: '-lz' + .unsafeFlags(["-Lfoobar"]), ] ), ] diff --git a/Sources/Build/BuildDescription/ProductBuildDescription.swift b/Sources/Build/BuildDescription/ProductBuildDescription.swift index 2b662984c8a..88fa223eee9 100644 --- a/Sources/Build/BuildDescription/ProductBuildDescription.swift +++ b/Sources/Build/BuildDescription/ProductBuildDescription.swift @@ -182,6 +182,12 @@ package final class ProductBuildDescription: SPMBuildCore.ProductBuildDescriptio var isLinkingStaticStdlib = false let triple = self.buildParameters.triple + + // radar://112671586 supress unnecessary warnings + if triple.isMacOSX { + args += ["-Xlinker", "-no_warn_duplicate_libraries"] + } + switch derivedProductType { case .macro: throw InternalError("macro not supported") // should never be reached diff --git a/Sources/XCBuildSupport/PIFBuilder.swift b/Sources/XCBuildSupport/PIFBuilder.swift index 153fd73a58a..dffbc7cbe9f 100644 --- a/Sources/XCBuildSupport/PIFBuilder.swift +++ b/Sources/XCBuildSupport/PIFBuilder.swift @@ -685,6 +685,9 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { impartedSettings[.OTHER_LDFLAGS, default: ["$(inherited)"]].append("-lc++") } + // radar://112671586 supress unnecessary warnings + impartedSettings[.OTHER_LDFLAGS, default: ["$(inherited)"]].append("-Wl,-no_warn_duplicate_libraries") + addSources(target.sources, to: pifTarget) // Handle the target's dependencies (but don't link against them). diff --git a/Tests/BuildTests/BuildPlanTests.swift b/Tests/BuildTests/BuildPlanTests.swift index a4c79b412df..bace9c92a9d 100644 --- a/Tests/BuildTests/BuildPlanTests.swift +++ b/Tests/BuildTests/BuildPlanTests.swift @@ -807,6 +807,7 @@ final class BuildPlanTests: XCTestCase { "-L", buildPath.pathString, "-o", buildPath.appending(components: "exe").pathString, "-module-name", "exe", + "-Xlinker", "-no_warn_duplicate_libraries", "-emit-executable", "-Xlinker", "-rpath", "-Xlinker", "@loader_path", "@\(buildPath.appending(components: "exe.product", "Objects.LinkFileList"))", @@ -1220,6 +1221,7 @@ final class BuildPlanTests: XCTestCase { "-L", buildPath.pathString, "-o", buildPath.appending(components: "exe").pathString, "-module-name", "exe", + "-Xlinker", "-no_warn_duplicate_libraries", "-emit-executable", "-Xlinker", "-dead_strip", "-Xlinker", "-rpath", "-Xlinker", "@loader_path", @@ -1310,6 +1312,7 @@ final class BuildPlanTests: XCTestCase { "-L", buildPath.pathString, "-o", buildPath.appending(components: "exe").pathString, "-module-name", "exe", + "-Xlinker", "-no_warn_duplicate_libraries", "-emit-executable", "-Xlinker", "-rpath", "-Xlinker", "@loader_path", "@\(buildPath.appending(components: "exe.product", "Objects.LinkFileList"))", @@ -1469,6 +1472,7 @@ final class BuildPlanTests: XCTestCase { "-L", buildPath.pathString, "-o", buildPath.appending(components: "exe").pathString, "-module-name", "exe", + "-Xlinker", "-no_warn_duplicate_libraries", "-emit-executable", "-Xlinker", "-rpath", "-Xlinker", "@loader_path", "@\(buildPath.appending(components: "exe.product", "Objects.LinkFileList"))", @@ -1659,6 +1663,7 @@ final class BuildPlanTests: XCTestCase { "-L", buildPath.pathString, "-o", buildPath.appending(components: "exe").pathString, "-module-name", "exe", + "-Xlinker", "-no_warn_duplicate_libraries", "-emit-executable", "-Xlinker", "-rpath", "-Xlinker", "@loader_path", "@\(buildPath.appending(components: "exe.product", "Objects.LinkFileList"))", @@ -1813,6 +1818,7 @@ final class BuildPlanTests: XCTestCase { "-L", buildPath.pathString, "-o", buildPath.appending(components: "exe").pathString, "-module-name", "exe", + "-Xlinker", "-no_warn_duplicate_libraries", "-emit-executable", "-Xlinker", "-rpath", "-Xlinker", "@loader_path", "@\(buildPath.appending(components: "exe.product", "Objects.LinkFileList"))", @@ -2061,6 +2067,7 @@ final class BuildPlanTests: XCTestCase { buildPath.appending(components: "PkgPackageTests.xctest", "Contents", "MacOS", "PkgPackageTests") .pathString, "-module-name", "PkgPackageTests", + "-Xlinker", "-no_warn_duplicate_libraries", "-Xlinker", "-bundle", "-Xlinker", "-rpath", "-Xlinker", "@loader_path/../../../", "@\(buildPath.appending(components: "PkgPackageTests.product", "Objects.LinkFileList"))", @@ -2159,6 +2166,7 @@ final class BuildPlanTests: XCTestCase { "-L", buildPath.pathString, "-o", buildPath.appending(components: "exe").pathString, "-module-name", "exe", + "-Xlinker", "-no_warn_duplicate_libraries", "-emit-executable", "-Xlinker", "-dead_strip", "-Xlinker", "-rpath", "-Xlinker", "@loader_path", @@ -2523,6 +2531,7 @@ final class BuildPlanTests: XCTestCase { "-L", buildPath.pathString, "-o", buildPath.appending(components: "exe").pathString, "-module-name", "exe", + "-Xlinker", "-no_warn_duplicate_libraries", "-emit-executable", "-Xlinker", "-rpath", "-Xlinker", "@loader_path", "@\(buildPath.appending(components: "exe.product", "Objects.LinkFileList"))", @@ -2670,6 +2679,7 @@ final class BuildPlanTests: XCTestCase { "-o", buildPath.appending(components: "Foo").pathString, "-module-name", "Foo", "-lBar-Baz", + "-Xlinker", "-no_warn_duplicate_libraries", "-emit-executable", "-Xlinker", "-rpath", "-Xlinker", "@loader_path", "@\(buildPath.appending(components: "Foo.product", "Objects.LinkFileList"))", @@ -2685,6 +2695,7 @@ final class BuildPlanTests: XCTestCase { "-L", buildPath.pathString, "-o", buildPath.appending(components: "libBar-Baz.dylib").pathString, "-module-name", "Bar_Baz", + "-Xlinker", "-no_warn_duplicate_libraries", "-emit-library", "-Xlinker", "-install_name", "-Xlinker", "@rpath/libBar-Baz.dylib", "-Xlinker", "-rpath", "-Xlinker", "@loader_path", @@ -2837,6 +2848,7 @@ final class BuildPlanTests: XCTestCase { "-L", buildPath.pathString, "-o", buildPath.appending(components: "liblib.dylib").pathString, "-module-name", "lib", + "-Xlinker", "-no_warn_duplicate_libraries", "-emit-library", "-Xlinker", "-install_name", "-Xlinker", "@rpath/liblib.dylib", "-Xlinker", "-rpath", "-Xlinker", "@loader_path", @@ -2976,6 +2988,7 @@ final class BuildPlanTests: XCTestCase { "-L", buildPath.pathString, "-o", buildPath.appending(components: "liblib.dylib").pathString, "-module-name", "lib", + "-Xlinker", "-no_warn_duplicate_libraries", "-emit-library", "-Xlinker", "-install_name", "-Xlinker", "@rpath/liblib.dylib", "-Xlinker", "-rpath", "-Xlinker", "@loader_path", @@ -2990,6 +3003,7 @@ final class BuildPlanTests: XCTestCase { "-L", buildPath.pathString, "-o", buildPath.appending(components: "exe").pathString, "-module-name", "exe", + "-Xlinker", "-no_warn_duplicate_libraries", "-emit-executable", "-Xlinker", "-rpath", "-Xlinker", "@loader_path", "@\(buildPath.appending(components: "exe.product", "Objects.LinkFileList"))", @@ -6087,6 +6101,7 @@ final class BuildPlanTests: XCTestCase { "-L", buildPath.pathString, "-o", buildPath.appending(components: "exe").pathString, "-module-name", "exe", + "-Xlinker", "-no_warn_duplicate_libraries", "-emit-executable", "@\(buildPath.appending(components: "exe.product", "Objects.LinkFileList"))", "-Xlinker", "-rpath", "-Xlinker", "/fake/path/lib/swift-5.5/macosx", diff --git a/Tests/BuildTests/BuildSystemDelegateTests.swift b/Tests/BuildTests/BuildSystemDelegateTests.swift index 2251dde6d44..4e44a42dafe 100644 --- a/Tests/BuildTests/BuildSystemDelegateTests.swift +++ b/Tests/BuildTests/BuildSystemDelegateTests.swift @@ -25,7 +25,7 @@ final class BuildSystemDelegateTests: XCTestCase { try XCTSkipIf(true, "test is only supported on macOS") #endif let (fullLog, _) = try executeSwiftBuild(fixturePath) - XCTAssertTrue(fullLog.contains("ld: warning: ignoring duplicate libraries: '-lz'"), "log didn't contain expected linker diagnostics") + XCTAssertTrue(fullLog.contains("ld: warning: search path 'foobar' not found"), "log didn't contain expected linker diagnostics") } } diff --git a/Tests/XCBuildSupportTests/PIFBuilderTests.swift b/Tests/XCBuildSupportTests/PIFBuilderTests.swift index f1171d8c706..c03033ecb9f 100644 --- a/Tests/XCBuildSupportTests/PIFBuilderTests.swift +++ b/Tests/XCBuildSupportTests/PIFBuilderTests.swift @@ -1312,6 +1312,7 @@ class PIFBuilderTests: XCTestCase { "-fmodule-map-file=$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)/FooLib1.modulemap" ]) XCTAssertEqual(settings[.OTHER_LDRFLAGS], []) + XCTAssertEqual(settings[.OTHER_LDFLAGS], ["$(inherited)", "-Wl,-no_warn_duplicate_libraries"]) } } @@ -1384,7 +1385,7 @@ class PIFBuilderTests: XCTestCase { "-fmodule-map-file=$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)/FooLib2.modulemap" ]) XCTAssertEqual(settings[.OTHER_LDRFLAGS], []) - XCTAssertEqual(settings[.OTHER_LDFLAGS], ["$(inherited)", "-lc++"]) + XCTAssertEqual(settings[.OTHER_LDFLAGS], ["$(inherited)", "-lc++", "-Wl,-no_warn_duplicate_libraries"]) XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS], [ "$(inherited)", "-Xcc", "-fmodule-map-file=$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)/FooLib2.modulemap" @@ -1463,6 +1464,7 @@ class PIFBuilderTests: XCTestCase { "-fmodule-map-file=$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)/BarLib.modulemap" ]) XCTAssertEqual(settings[.OTHER_LDRFLAGS], []) + XCTAssertEqual(settings[.OTHER_LDFLAGS], ["$(inherited)", "-Wl,-no_warn_duplicate_libraries"]) XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS], [ "$(inherited)", "-Xcc", "-fmodule-map-file=$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)/BarLib.modulemap" @@ -2406,7 +2408,7 @@ class PIFBuilderTests: XCTestCase { target.checkImpartedBuildSettings { settings in XCTAssertEqual(settings[.GCC_PREPROCESSOR_DEFINITIONS], nil) XCTAssertEqual(settings[.HEADER_SEARCH_PATHS], nil) - XCTAssertEqual(settings[.OTHER_LDFLAGS], ["$(inherited)", "-lz"]) + XCTAssertEqual(settings[.OTHER_LDFLAGS], ["$(inherited)", "-Wl,-no_warn_duplicate_libraries", "-lz"]) XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS], nil) } } From adb8ea043357818da7609d41fd92327eb5b34fb6 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Wed, 20 Mar 2024 14:39:25 +0100 Subject: [PATCH 050/159] Write output-file-map.json atomically (#7406) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are two subtle changes in behavior here: - This call will fail if the file system doesn’t support atomic operations. I’m not sure if we need to support file systems that don’t allow atomic operations here. rdar://124727242 --- .../Build/BuildDescription/SwiftTargetBuildDescription.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift index b59b328f3ca..cbc5bed2f83 100644 --- a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift @@ -779,7 +779,8 @@ package final class SwiftTargetBuildDescription { content += "}\n" - try self.fileSystem.writeFileContents(path, string: content) + try fileSystem.createDirectory(path.parentDirectory, recursive: true) + try self.fileSystem.writeFileContents(path, bytes: .init(encodingAsUTF8: content), atomically: true) return path } From d3ee3f63ca6ccdd0f8672a9a0d5714dbdb00d4aa Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Wed, 20 Mar 2024 22:44:39 +0100 Subject: [PATCH 051/159] Fix a memory leak in `DownloadTaskManager` and `DataTaskManager` (#7408) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `DownloadTaskManager` had a strong reference to `URLSession`, which retained its delegate, which was the `DownloadTaskManager`. This formed a retain cycle. Make the reference from `URLSession` to the delegate weak and only keep `DownloadTaskManager` alive as long as `DownloadTask`s need it. This causes the `DownloadTaskManager` to be deallocated once nobody holds a reference to it anymore and all the tasks it manages are finished. Finally, we need to call `invalidate` on the `URLSession` to tell it that we’re done and that it should release its delegate (which is now the `WeakDownloadTaskManager`wrapper). This retain cycle was causing a leak in sourcekit-lsp. I verified that the leak no longer exists with this patch. rdar://125012584 --- .../HTTPClient/URLSessionHTTPClient.swift | 130 +++++++++++++++++- 1 file changed, 123 insertions(+), 7 deletions(-) diff --git a/Sources/Basics/HTTPClient/URLSessionHTTPClient.swift b/Sources/Basics/HTTPClient/URLSessionHTTPClient.swift index d6302003d2a..a3d4102da64 100644 --- a/Sources/Basics/HTTPClient/URLSessionHTTPClient.swift +++ b/Sources/Basics/HTTPClient/URLSessionHTTPClient.swift @@ -87,7 +87,60 @@ final class URLSessionHTTPClient { } } -private class DataTaskManager: NSObject, URLSessionDataDelegate { +/// A weak wrapper around `DataTaskManager` that conforms to `URLSessionDataDelegate`. +/// +/// This ensures that we don't get a retain cycle between `DataTaskManager.session` -> `URLSession.delegate` -> `DataTaskManager`. +/// +/// The `DataTaskManager` is being kept alive by a reference from all `DataTask`s that it manages. Once all the +/// `DataTasks` have finished and are deallocated, `DataTaskManager` will get deinitialized, which invalidates the +/// session, which then lets go of `WeakDataTaskManager`. +private class WeakDataTaskManager: NSObject, URLSessionDataDelegate { + private weak var dataTaskManager: DataTaskManager? + + init(_ dataTaskManager: DataTaskManager? = nil) { + self.dataTaskManager = dataTaskManager + } + + func urlSession( + _ session: URLSession, + dataTask: URLSessionDataTask, + didReceive response: URLResponse, + completionHandler: @escaping (URLSession.ResponseDisposition) -> Void + ) { + dataTaskManager?.urlSession( + session, + dataTask: dataTask, + didReceive: response, + completionHandler: completionHandler + ) + } + + func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { + dataTaskManager?.urlSession(session, dataTask: dataTask, didReceive: data) + } + + func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { + dataTaskManager?.urlSession(session, task: task, didCompleteWithError: error) + } + + func urlSession( + _ session: URLSession, + task: URLSessionTask, + willPerformHTTPRedirection response: HTTPURLResponse, + newRequest request: URLRequest, + completionHandler: @escaping (URLRequest?) -> Void + ) { + dataTaskManager?.urlSession( + session, + task: task, + willPerformHTTPRedirection: response, + newRequest: request, + completionHandler: completionHandler + ) + } +} + +private class DataTaskManager { private var tasks = ThreadSafeKeyValueStore() private let delegateQueue: OperationQueue private var session: URLSession! @@ -96,8 +149,11 @@ private class DataTaskManager: NSObject, URLSessionDataDelegate { self.delegateQueue = OperationQueue() self.delegateQueue.name = "org.swift.swiftpm.urlsession-http-client-data-delegate" self.delegateQueue.maxConcurrentOperationCount = 1 - super.init() - self.session = URLSession(configuration: configuration, delegate: self, delegateQueue: self.delegateQueue) + self.session = URLSession(configuration: configuration, delegate: WeakDataTaskManager(self), delegateQueue: self.delegateQueue) + } + + deinit { + session.finishTasksAndInvalidate() } func makeTask( @@ -110,6 +166,7 @@ private class DataTaskManager: NSObject, URLSessionDataDelegate { self.tasks[task.taskIdentifier] = DataTask( task: task, progressHandler: progress, + dataTaskManager: self, completionHandler: completion, authorizationProvider: authorizationProvider ) @@ -192,6 +249,11 @@ private class DataTaskManager: NSObject, URLSessionDataDelegate { class DataTask { let task: URLSessionDataTask let completionHandler: LegacyHTTPClient.CompletionHandler + /// A strong reference to keep the `DataTaskManager` alive so it can handle the callbacks from the + /// `URLSession`. + /// + /// See comment on `WeakDataTaskManager`. + let dataTaskManager: DataTaskManager let progressHandler: LegacyHTTPClient.ProgressHandler? let authorizationProvider: LegacyHTTPClientConfiguration.AuthorizationProvider? @@ -202,18 +264,61 @@ private class DataTaskManager: NSObject, URLSessionDataDelegate { init( task: URLSessionDataTask, progressHandler: LegacyHTTPClient.ProgressHandler?, + dataTaskManager: DataTaskManager, completionHandler: @escaping LegacyHTTPClient.CompletionHandler, authorizationProvider: LegacyHTTPClientConfiguration.AuthorizationProvider? ) { self.task = task self.progressHandler = progressHandler + self.dataTaskManager = dataTaskManager self.completionHandler = completionHandler self.authorizationProvider = authorizationProvider } } } -private class DownloadTaskManager: NSObject, URLSessionDownloadDelegate { +/// This uses the same pattern as `WeakDataTaskManager`. See comment on that type. +private class WeakDownloadTaskManager: NSObject, URLSessionDownloadDelegate { + private weak var downloadTaskManager: DownloadTaskManager? + + init(_ downloadTaskManager: DownloadTaskManager? = nil) { + self.downloadTaskManager = downloadTaskManager + } + + func urlSession( + _ session: URLSession, + downloadTask: URLSessionDownloadTask, + didWriteData bytesWritten: Int64, + totalBytesWritten: Int64, + totalBytesExpectedToWrite: Int64 + ) { + downloadTaskManager?.urlSession( + session, + downloadTask: downloadTask, + didWriteData: bytesWritten, + totalBytesWritten: totalBytesWritten, + totalBytesExpectedToWrite: totalBytesExpectedToWrite + ) + } + + func urlSession( + _ session: URLSession, + downloadTask: URLSessionDownloadTask, + didFinishDownloadingTo location: URL + ) { + downloadTaskManager?.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: location) + } + + func urlSession( + _ session: URLSession, + task downloadTask: URLSessionTask, + didCompleteWithError error: Error? + ) { + downloadTaskManager?.urlSession(session, task: downloadTask, didCompleteWithError: error) + } +} + +private class DownloadTaskManager { private var tasks = ThreadSafeKeyValueStore() private let delegateQueue: OperationQueue private var session: URLSession! @@ -222,8 +327,11 @@ private class DownloadTaskManager: NSObject, URLSessionDownloadDelegate { self.delegateQueue = OperationQueue() self.delegateQueue.name = "org.swift.swiftpm.urlsession-http-client-download-delegate" self.delegateQueue.maxConcurrentOperationCount = 1 - super.init() - self.session = URLSession(configuration: configuration, delegate: self, delegateQueue: self.delegateQueue) + self.session = URLSession(configuration: configuration, delegate: WeakDownloadTaskManager(self), delegateQueue: self.delegateQueue) + } + + deinit { + session.finishTasksAndInvalidate() } func makeTask( @@ -238,6 +346,7 @@ private class DownloadTaskManager: NSObject, URLSessionDownloadDelegate { task: task, fileSystem: fileSystem, destination: destination, + downloadTaskManager: self, progressHandler: progress, completionHandler: completion ) @@ -314,8 +423,13 @@ private class DownloadTaskManager: NSObject, URLSessionDownloadDelegate { let task: URLSessionDownloadTask let fileSystem: FileSystem let destination: AbsolutePath - let completionHandler: LegacyHTTPClient.CompletionHandler + /// A strong reference to keep the `DownloadTaskManager` alive so it can handle the callbacks from the + /// `URLSession`. + /// + /// See comment on `WeakDownloadTaskManager`. + private let downloadTaskManager: DownloadTaskManager let progressHandler: LegacyHTTPClient.ProgressHandler? + let completionHandler: LegacyHTTPClient.CompletionHandler var moveFileError: Error? @@ -323,12 +437,14 @@ private class DownloadTaskManager: NSObject, URLSessionDownloadDelegate { task: URLSessionDownloadTask, fileSystem: FileSystem, destination: AbsolutePath, + downloadTaskManager: DownloadTaskManager, progressHandler: LegacyHTTPClient.ProgressHandler?, completionHandler: @escaping LegacyHTTPClient.CompletionHandler ) { self.task = task self.fileSystem = fileSystem self.destination = destination + self.downloadTaskManager = downloadTaskManager self.progressHandler = progressHandler self.completionHandler = completionHandler } From 9d48dc70aab03a1824ee63abdf105212e08b1dbd Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 21 Mar 2024 23:39:02 +0000 Subject: [PATCH 052/159] Fix doc comment typo in `Sandbox.swift` (#7418) `standbox` -> `sandbox` --- Sources/Basics/Sandbox.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Basics/Sandbox.swift b/Sources/Basics/Sandbox.swift index f8a32d46874..16e981e540c 100644 --- a/Sources/Basics/Sandbox.swift +++ b/Sources/Basics/Sandbox.swift @@ -45,7 +45,7 @@ public enum Sandbox { /// - Parameters: /// - command: The command line to sandbox (including executable as first argument) /// - fileSystem: The file system instance to use. - /// - strictness: The basic strictness level of the standbox. + /// - strictness: The basic strictness level of the sandbox. /// - writableDirectories: Paths under which writing should be allowed, even if they would otherwise be read-only based on the strictness or paths in `readOnlyDirectories`. /// - readOnlyDirectories: Paths under which writing should be denied, even if they would have otherwise been allowed by the rules implied by the strictness level. public static func apply( From a809fb546ad6e50257e522b518a9b848ff86aecc Mon Sep 17 00:00:00 2001 From: miharu <35392604+mnaruse@users.noreply.github.com> Date: Mon, 25 Mar 2024 21:41:33 +0900 Subject: [PATCH 053/159] PackageModelTests: fix warnings (#7421) Resolve warnings in `Tests/PackageModelTests/MinimumDeploymentTargetTests.swift` . ### Motivation: 3 warnings were displayed indicating that a deprecated function is being used and suggesting the use of an alternative function. ### Modifications: Following the suggestion, the warning was resolved. ### Result: The following warnings have been resolved: > `init(arguments:environment:exitStatus:output:stderrOutput:)` is deprecated: use `init(arguments:environmentBlock:exitStatus:output:stderrOutput:)` --- Tests/PackageModelTests/MinimumDeploymentTargetTests.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/PackageModelTests/MinimumDeploymentTargetTests.swift b/Tests/PackageModelTests/MinimumDeploymentTargetTests.swift index 42828726b2b..bcb2e58d62b 100644 --- a/Tests/PackageModelTests/MinimumDeploymentTargetTests.swift +++ b/Tests/PackageModelTests/MinimumDeploymentTargetTests.swift @@ -22,7 +22,7 @@ class MinimumDeploymentTargetTests: XCTestCase { try XCTSkipIf(true, "test is only supported on macOS") #endif let result = ProcessResult(arguments: [], - environment: [:], + environmentBlock: [:], exitStatus: .terminated(code: 0), output: "".asResult, stderrOutput: "xcodebuild: error: SDK \"macosx\" cannot be located.".asResult) @@ -36,7 +36,7 @@ class MinimumDeploymentTargetTests: XCTestCase { try XCTSkipIf(true, "test is only supported on macOS") #endif let result = ProcessResult(arguments: [], - environment: [:], + environmentBlock: [:], exitStatus: .terminated(code: 0), output: "some string".asResult, stderrOutput: "".asResult) @@ -50,7 +50,7 @@ class MinimumDeploymentTargetTests: XCTestCase { try XCTSkipIf(true, "test is only supported on macOS") #endif let result = ProcessResult(arguments: [], - environment: [:], + environmentBlock: [:], exitStatus: .terminated(code: 0), output: .failure(DummyError()), stderrOutput: "".asResult) From 2bc24f05e85b54ff9f26622a97e2cf9b3b40f50b Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Mon, 25 Mar 2024 23:24:17 +0100 Subject: [PATCH 054/159] Use `arguments` instead of `argumentList` in the macro template (#7422) `argumentList` has been deprecated. We should use `arguments` instead. --- Sources/Workspace/InitPackage.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Workspace/InitPackage.swift b/Sources/Workspace/InitPackage.swift index 77c1dadb46a..28db61c183a 100644 --- a/Sources/Workspace/InitPackage.swift +++ b/Sources/Workspace/InitPackage.swift @@ -836,7 +836,7 @@ public final class InitPackage { of node: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext ) -> ExprSyntax { - guard let argument = node.argumentList.first?.expression else { + guard let argument = node.arguments.first?.expression else { fatalError("compiler bug: the macro does not have any arguments") } From 601d040cb7a060895a940e3d989138667e49cff0 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Tue, 26 Mar 2024 15:00:03 -0400 Subject: [PATCH 055/159] On Darwin, allow XCTest to be missing if we're only building swift-testing tests. (#7426) This PR removes the constraint on Darwin that XCTest.framework must be present in order to build tests using swift-testing. On Darwin, XCTest is included as a framework inside Xcode, but if a developer installs the Xcode Command Line Tools instead of the full IDE, XCTest is not included. They then get a diagnostic of the form: > error: XCTest not available: terminated(1): /usr/bin/xcrun --sdk macosx --show-sdk-platform-path output: > xcrun: error: unable to lookup item 'PlatformPath' from command line tools installation > xcrun: error: unable to lookup item 'PlatformPath' in SDK '/Library/Developer/CommandLineTools/SDKs/MacOSX14.0.sdk' Which is a poor experience if they aren't even using XCTest. This change, as a (positive) side effect, suppresses the same diagnostic when running commands that are not usually dependent on the presence of XCTest such as `swift build`. Note that swift-corelibs-xctest is not supported on Darwin, so installing the Xcode Command Line Tools and adding an explicit dependency on swift-corelibs-xctest will not produce a functional test target bundle. Supporting swift-corelibs-xctest on Darwin is a potential future direction. Automated testing for this change is difficult because it relies on a build environment that is not supported in CI (namely the presence of the CL tools but not Xcode nor XCTest.framework.) I have manually tested the change against swift-testing's own test target. A separate PR will be necessary in swift-testing to remove some remaining XCTest dependencies. Those changes are not covered by this PR. Resolves rdar://125372431. --- Sources/Commands/SwiftTestCommand.swift | 22 +++++-- .../Commands/Utilities/TestingSupport.swift | 11 ++-- Sources/PackageModel/SwiftSDKs/SwiftSDK.swift | 64 ++++++++++++++++--- 3 files changed, 76 insertions(+), 21 deletions(-) diff --git a/Sources/Commands/SwiftTestCommand.swift b/Sources/Commands/SwiftTestCommand.swift index bbccc0c2260..bd2ebf02208 100644 --- a/Sources/Commands/SwiftTestCommand.swift +++ b/Sources/Commands/SwiftTestCommand.swift @@ -40,7 +40,8 @@ private enum TestError: Swift.Error { case testProductNotFound(productName: String) case productIsNotTest(productName: String) case multipleTestProducts([String]) - case xctestNotAvailable + case xctestNotAvailable(reason: String) + case xcodeNotInstalled } extension TestError: CustomStringConvertible { @@ -57,8 +58,10 @@ extension TestError: CustomStringConvertible { return "invalid list test JSON structure, produced by \(context)\(underlying)" case .multipleTestProducts(let products): return "found multiple test products: \(products.joined(separator: ", ")); use --test-product to select one" - case .xctestNotAvailable: - return "XCTest not available" + case let .xctestNotAvailable(reason): + return "XCTest not available: \(reason)" + case .xcodeNotInstalled: + return "XCTest not available; download and install Xcode to use XCTest on this platform" } } } @@ -203,9 +206,14 @@ package struct SwiftTestCommand: AsyncSwiftCommand { private func xctestRun(_ swiftCommandState: SwiftCommandState) async throws { // validate XCTest available on darwin based systems let toolchain = try swiftCommandState.getTargetToolchain() - let isHostTestingAvailable = try swiftCommandState.getHostToolchain().swiftSDK.supportsTesting - if (toolchain.targetTriple.isDarwin() && toolchain.xctestPath == nil) || !isHostTestingAvailable { - throw TestError.xctestNotAvailable + if case let .unsupported(reason) = try swiftCommandState.getHostToolchain().swiftSDK.xctestSupport { + if let reason { + throw TestError.xctestNotAvailable(reason: reason) + } else { + throw TestError.xcodeNotInstalled + } + } else if toolchain.targetTriple.isDarwin() && toolchain.xctestPath == nil { + throw TestError.xcodeNotInstalled } let buildParameters = try swiftCommandState.buildParametersForTest(options: self.options, library: .xctest) @@ -814,7 +822,7 @@ final class TestRunner { #if os(macOS) if library == .xctest { guard let xctestPath = self.toolchain.xctestPath else { - throw TestError.xctestNotAvailable + throw TestError.xcodeNotInstalled } args = [xctestPath.pathString] args += additionalArguments diff --git a/Sources/Commands/Utilities/TestingSupport.swift b/Sources/Commands/Utilities/TestingSupport.swift index 43f889fc309..d7643d330e7 100644 --- a/Sources/Commands/Utilities/TestingSupport.swift +++ b/Sources/Commands/Utilities/TestingSupport.swift @@ -178,11 +178,12 @@ enum TestingSupport { #endif return env #else - // Add the sdk platform path if we have it. If this is not present, we might always end up failing. - let sdkPlatformFrameworksPath = try SwiftSDK.sdkPlatformFrameworkPaths() - // appending since we prefer the user setting (if set) to the one we inject - env.appendPath("DYLD_FRAMEWORK_PATH", value: sdkPlatformFrameworksPath.fwk.pathString) - env.appendPath("DYLD_LIBRARY_PATH", value: sdkPlatformFrameworksPath.lib.pathString) + // Add the sdk platform path if we have it. + if let sdkPlatformFrameworksPath = try? SwiftSDK.sdkPlatformFrameworkPaths() { + // appending since we prefer the user setting (if set) to the one we inject + env.appendPath("DYLD_FRAMEWORK_PATH", value: sdkPlatformFrameworksPath.fwk.pathString) + env.appendPath("DYLD_LIBRARY_PATH", value: sdkPlatformFrameworksPath.lib.pathString) + } // Fast path when no sanitizers are enabled. if sanitizers.isEmpty { diff --git a/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift b/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift index 3a3ec0452f3..665c4fe1351 100644 --- a/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift +++ b/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift @@ -147,7 +147,29 @@ public struct SwiftSDK: Equatable { public var architectures: [String]? = nil /// Whether or not the receiver supports testing. - public let supportsTesting: Bool + @available(*, deprecated, message: "Use `xctestSupport` instead") + public var supportsTesting: Bool { + if case .supported = xctestSupport { + return true + } + return false + } + + /// Whether or not the receiver supports testing using XCTest. + package enum XCTestSupport: Sendable, Equatable { + /// XCTest is supported. + case supported + + /// XCTest is not supported. + /// + /// - Parameters: + /// - reason: A string explaining why XCTest is not supported. If + /// `nil`, no additional information is available. + case unsupported(reason: String?) + } + + /// Whether or not the receiver supports using XCTest. + package let xctestSupport: XCTestSupport /// Root directory path of the SDK used to compile for the target triple. @available(*, deprecated, message: "use `pathsConfiguration.sdkRootPath` instead") @@ -418,18 +440,43 @@ public struct SwiftSDK: Equatable { } /// Creates a Swift SDK with the specified properties. + @available(*, deprecated, message: "use `init(hostTriple:targetTriple:toolset:pathsConfiguration:xctestSupport:)` instead") public init( hostTriple: Triple? = nil, targetTriple: Triple? = nil, toolset: Toolset, pathsConfiguration: PathsConfiguration, - supportsTesting: Bool = true + supportsTesting: Bool + ) { + let xctestSupport: XCTestSupport + if supportsTesting { + xctestSupport = .supported + } else { + xctestSupport = .unsupported(reason: nil) + } + + self.init( + hostTriple: hostTriple, + targetTriple: targetTriple, + toolset: toolset, + pathsConfiguration: pathsConfiguration, + xctestSupport: xctestSupport + ) + } + + /// Creates a Swift SDK with the specified properties. + package init( + hostTriple: Triple? = nil, + targetTriple: Triple? = nil, + toolset: Toolset, + pathsConfiguration: PathsConfiguration, + xctestSupport: XCTestSupport = .supported ) { self.hostTriple = hostTriple self.targetTriple = targetTriple self.toolset = toolset self.pathsConfiguration = pathsConfiguration - self.supportsTesting = supportsTesting + self.xctestSupport = xctestSupport } /// Returns the bin directory for the host. @@ -496,7 +543,7 @@ public struct SwiftSDK: Equatable { #endif // Compute common arguments for clang and swift. - let supportsTesting: Bool + let xctestSupport: XCTestSupport var extraCCFlags: [String] = [] var extraSwiftCFlags: [String] = [] #if os(macOS) @@ -506,13 +553,12 @@ public struct SwiftSDK: Equatable { extraSwiftCFlags += ["-F", sdkPaths.fwk.pathString] extraSwiftCFlags += ["-I", sdkPaths.lib.pathString] extraSwiftCFlags += ["-L", sdkPaths.lib.pathString] - supportsTesting = true + xctestSupport = .supported } catch { - supportsTesting = false - observabilityScope?.emit(warning: "could not determine XCTest paths: \(error)") + xctestSupport = .unsupported(reason: String(describing: error)) } #else - supportsTesting = true + xctestSupport = .supported #endif #if !os(Windows) @@ -528,7 +574,7 @@ public struct SwiftSDK: Equatable { rootPaths: [binDir] ), pathsConfiguration: .init(sdkRootPath: sdkPath), - supportsTesting: supportsTesting + xctestSupport: xctestSupport ) } From 451fbb265af8d676a0827933394a722cc8e7c808 Mon Sep 17 00:00:00 2001 From: coffmark <52638834+coffmark@users.noreply.github.com> Date: Wed, 27 Mar 2024 20:26:25 +0900 Subject: [PATCH 056/159] Fix indentation in `Product.swift` (#7427) ### Modifications: Fix indentation in `Product.swift` ### Result: - [x] I executed SwiftFormat and checked that the indentation remained corrected. --- Sources/PackageDescription/Product.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Sources/PackageDescription/Product.swift b/Sources/PackageDescription/Product.swift index d022eb91c50..4250817457a 100644 --- a/Sources/PackageDescription/Product.swift +++ b/Sources/PackageDescription/Product.swift @@ -69,7 +69,6 @@ public class Product { /// The executable product of a Swift package. public final class Executable: Product { - /// The names of the targets in this product. public let targets: [String] @@ -149,7 +148,7 @@ public class Product { /// - name: The name of the executable product. /// - targets: The targets to bundle into an executable product. /// - Returns: A `Product` instance. -public static func executable( + public static func executable( name: String, targets: [String] ) -> Product { @@ -167,7 +166,7 @@ public static func executable( /// - name: The name of the plugin product. /// - targets: The plugin targets to vend as a product. /// - Returns: A `Product` instance. -@available(_PackageDescription, introduced: 5.5) + @available(_PackageDescription, introduced: 5.5) public static func plugin( name: String, targets: [String] From 5d177e6b8f746d284c4c0c7aba9c10ff777f9ca6 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 25 Mar 2024 13:42:25 -0700 Subject: [PATCH 057/159] Give clang target description access to the package it's associated with We need this information to set `-w` and other package specific options. --- .../ClangTargetBuildDescription.swift | 6 +++++ Sources/Build/BuildPlan/BuildPlan.swift | 5 ++++ .../ClangTargetBuildDescriptionTests.swift | 27 ++++++++++++++++++- 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift b/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift index a95ab57a944..70821cd82e4 100644 --- a/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// import Basics +import PackageGraph import PackageLoading import PackageModel import struct PackageGraph.ModulesGraph @@ -23,6 +24,9 @@ import enum TSCBasic.ProcessEnv /// Target description for a Clang target i.e. C language family target. package final class ClangTargetBuildDescription { + /// The package this target belongs to. + package let package: ResolvedPackage + /// The target described by this target. package let target: ResolvedTarget @@ -109,6 +113,7 @@ package final class ClangTargetBuildDescription { /// Create a new target description with target and build parameters. init( + package: ResolvedPackage, target: ResolvedTarget, toolsVersion: ToolsVersion, additionalFileRules: [FileRuleDescription] = [], @@ -122,6 +127,7 @@ package final class ClangTargetBuildDescription { throw InternalError("underlying target type mismatch \(target)") } + self.package = package self.clangTarget = clangTarget self.fileSystem = fileSystem self.target = target diff --git a/Sources/Build/BuildPlan/BuildPlan.swift b/Sources/Build/BuildPlan/BuildPlan.swift index dced6904e49..5f6b8389c3f 100644 --- a/Sources/Build/BuildPlan/BuildPlan.swift +++ b/Sources/Build/BuildPlan/BuildPlan.swift @@ -394,8 +394,13 @@ public class BuildPlan: SPMBuildCore.BuildPlan { ) ) case is ClangTarget: + guard let package = graph.package(for: target) else { + throw InternalError("package not found for \(target)") + } + targetMap[target.id] = try .clang( ClangTargetBuildDescription( + package: package, target: target, toolsVersion: toolsVersion, additionalFileRules: additionalFileRules, diff --git a/Tests/BuildTests/ClangTargetBuildDescriptionTests.swift b/Tests/BuildTests/ClangTargetBuildDescriptionTests.swift index cc052cfe146..bc8047142be 100644 --- a/Tests/BuildTests/ClangTargetBuildDescriptionTests.swift +++ b/Tests/BuildTests/ClangTargetBuildDescriptionTests.swift @@ -49,8 +49,33 @@ final class ClangTargetBuildDescriptionTests: XCTestCase { private func makeTargetBuildDescription() throws -> ClangTargetBuildDescription { let observability = ObservabilitySystem.makeForTesting(verbose: false) + + let manifest = Manifest.createRootManifest( + displayName: "dummy", + toolsVersion: .v5, + targets: [try TargetDescription(name: "dummy")] + ) + + let target = try makeResolvedTarget() + + let package = Package(identity: .plain("dummy"), + manifest: manifest, + path: .root, + targets: [target.underlying], + products: [], + targetSearchPath: .root, + testTargetSearchPath: .root) + return try ClangTargetBuildDescription( - target: try makeResolvedTarget(), + package: .init(underlying: package, + defaultLocalization: nil, + supportedPlatforms: [], + dependencies: [], + targets: [target], + products: [], + registryMetadata: nil, + platformVersionProvider: .init(implementation: .minimumDeploymentTargetDefault)), + target: target, toolsVersion: .current, buildParameters: mockBuildParameters( toolchain: try UserToolchain.default, From 9a7dbb46271eb1cedcf1d461dee582279a7bd274 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 25 Mar 2024 16:19:39 -0700 Subject: [PATCH 058/159] Add a workaround for swift-corelibs-foundation on Linux/Android CoreFoundation depends on dispatch, this dependency is implicit and dispatch itself is bundled with swift toolchains. Let's add a package specific workaround for Linux/Android targets to add toolchain resources directory to search paths of clang build targets. --- .../ClangTargetBuildDescription.swift | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift b/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift index 70821cd82e4..09548888d5c 100644 --- a/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift @@ -311,6 +311,18 @@ package final class ClangTargetBuildDescription { args += ["-I", includeSearchPath.pathString] } + // FIXME: Remove this once it becomes possible to express this dependency in a package manifest. + // + // On Linux/Android swift-corelibs-foundation depends on dispatch library which is + // currently shipped with the Swift toolchain. + if (triple.isLinux() || triple.isAndroid()) && self.package.id == .plain("swift-corelibs-foundation") { + let swiftCompilerPath = self.buildParameters.toolchain.swiftCompilerPath + let toolchainResourcesPath = swiftCompilerPath.parentDirectory + .parentDirectory + .appending(components: ["lib", "swift"]) + args += ["-I", toolchainResourcesPath.pathString] + } + return args } From 9f8cf31e3e81bc2962117820940772f5b7ade634 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 26 Mar 2024 10:21:38 -0700 Subject: [PATCH 059/159] NFC: Add a test-case for swift-corelibs-foundation workaround --- .../ClangTargetBuildDescriptionTests.swift | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/Tests/BuildTests/ClangTargetBuildDescriptionTests.swift b/Tests/BuildTests/ClangTargetBuildDescriptionTests.swift index bc8047142be..837b8aca379 100644 --- a/Tests/BuildTests/ClangTargetBuildDescriptionTests.swift +++ b/Tests/BuildTests/ClangTargetBuildDescriptionTests.swift @@ -14,15 +14,40 @@ import Basics @testable import Build import PackageGraph import PackageModel +import SPMBuildCore import SPMTestSupport import XCTest final class ClangTargetBuildDescriptionTests: XCTestCase { func testClangIndexStorePath() throws { - let targetDescription = try makeTargetBuildDescription() + let targetDescription = try makeTargetBuildDescription("test") XCTAssertTrue(try targetDescription.basicArguments().contains("-index-store-path")) } + func testSwiftCorelibsFoundationIncludeWorkaround() throws { + let macosParameters = mockBuildParameters( + toolchain: try UserToolchain.default, + targetTriple: .macOS) + let linuxParameters = mockBuildParameters( + toolchain: try UserToolchain.default, + targetTriple: .arm64Linux) + let androidParameters = mockBuildParameters( + toolchain: try UserToolchain.default, + targetTriple: .arm64Android) + + let macDescription = try makeTargetBuildDescription("swift-corelibs-foundation", + buildParameters: macosParameters) + XCTAssertFalse(try macDescription.basicArguments().contains("\(macosParameters.toolchain.swiftResourcesPath!)")) + + let linuxDescription = try makeTargetBuildDescription("swift-corelibs-foundation", + buildParameters: linuxParameters) + XCTAssertTrue(try linuxDescription.basicArguments().contains("\(linuxParameters.toolchain.swiftResourcesPath!)")) + + let androidDescription = try makeTargetBuildDescription("swift-corelibs-foundation", + buildParameters: androidParameters) + XCTAssertTrue(try androidDescription.basicArguments().contains("\(androidParameters.toolchain.swiftResourcesPath!)")) + } + private func makeClangTarget() throws -> ClangTarget { try ClangTarget( name: "dummy", @@ -47,7 +72,8 @@ final class ClangTargetBuildDescriptionTests: XCTestCase { ) } - private func makeTargetBuildDescription() throws -> ClangTargetBuildDescription { + private func makeTargetBuildDescription(_ packageName: String, + buildParameters: BuildParameters? = nil) throws -> ClangTargetBuildDescription { let observability = ObservabilitySystem.makeForTesting(verbose: false) let manifest = Manifest.createRootManifest( @@ -58,7 +84,7 @@ final class ClangTargetBuildDescriptionTests: XCTestCase { let target = try makeResolvedTarget() - let package = Package(identity: .plain("dummy"), + let package = Package(identity: .plain(packageName), manifest: manifest, path: .root, targets: [target.underlying], @@ -77,7 +103,7 @@ final class ClangTargetBuildDescriptionTests: XCTestCase { platformVersionProvider: .init(implementation: .minimumDeploymentTargetDefault)), target: target, toolsVersion: .current, - buildParameters: mockBuildParameters( + buildParameters: buildParameters ?? mockBuildParameters( toolchain: try UserToolchain.default, indexStoreMode: .on ), From 42e86f9376c88f2d19b85bdee8d3c9c0f7252650 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 26 Mar 2024 10:46:18 -0700 Subject: [PATCH 060/159] NFC: Use mock toolchain for swift-corelib-foundation workaround testing --- Sources/SPMTestSupport/MockBuildTestHelper.swift | 6 ++++-- .../ClangTargetBuildDescriptionTests.swift | 15 ++++++--------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/Sources/SPMTestSupport/MockBuildTestHelper.swift b/Sources/SPMTestSupport/MockBuildTestHelper.swift index c090266f00a..bce133aacdb 100644 --- a/Sources/SPMTestSupport/MockBuildTestHelper.swift +++ b/Sources/SPMTestSupport/MockBuildTestHelper.swift @@ -30,7 +30,7 @@ package struct MockToolchain: PackageModel.Toolchain { package let swiftCompilerPath = AbsolutePath("/fake/path/to/swiftc") package let includeSearchPaths = [AbsolutePath]() package let librarySearchPaths = [AbsolutePath]() - package let swiftResourcesPath: AbsolutePath? = nil + package let swiftResourcesPath: AbsolutePath? package let swiftStaticResourcesPath: AbsolutePath? = nil package let isSwiftDevelopmentToolchain = false package let sdkRootPath: AbsolutePath? = nil @@ -51,7 +51,9 @@ package struct MockToolchain: PackageModel.Toolchain { #endif } - package init() {} + package init(swiftResourcesPath: AbsolutePath? = nil) { + self.swiftResourcesPath = swiftResourcesPath + } } extension Basics.Triple { diff --git a/Tests/BuildTests/ClangTargetBuildDescriptionTests.swift b/Tests/BuildTests/ClangTargetBuildDescriptionTests.swift index 837b8aca379..a7c4cbd6fbc 100644 --- a/Tests/BuildTests/ClangTargetBuildDescriptionTests.swift +++ b/Tests/BuildTests/ClangTargetBuildDescriptionTests.swift @@ -25,15 +25,11 @@ final class ClangTargetBuildDescriptionTests: XCTestCase { } func testSwiftCorelibsFoundationIncludeWorkaround() throws { - let macosParameters = mockBuildParameters( - toolchain: try UserToolchain.default, - targetTriple: .macOS) - let linuxParameters = mockBuildParameters( - toolchain: try UserToolchain.default, - targetTriple: .arm64Linux) - let androidParameters = mockBuildParameters( - toolchain: try UserToolchain.default, - targetTriple: .arm64Android) + let toolchain = MockToolchain(swiftResourcesPath: AbsolutePath("/fake/path/lib/swift")) + + let macosParameters = mockBuildParameters(toolchain: toolchain, targetTriple: .macOS) + let linuxParameters = mockBuildParameters(toolchain: toolchain, targetTriple: .arm64Linux) + let androidParameters = mockBuildParameters(toolchain: toolchain, targetTriple: .arm64Android) let macDescription = try makeTargetBuildDescription("swift-corelibs-foundation", buildParameters: macosParameters) @@ -41,6 +37,7 @@ final class ClangTargetBuildDescriptionTests: XCTestCase { let linuxDescription = try makeTargetBuildDescription("swift-corelibs-foundation", buildParameters: linuxParameters) + print(try linuxDescription.basicArguments()) XCTAssertTrue(try linuxDescription.basicArguments().contains("\(linuxParameters.toolchain.swiftResourcesPath!)")) let androidDescription = try makeTargetBuildDescription("swift-corelibs-foundation", From f17f58478be13384679b2ae2be32e3bd1be797e1 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 25 Mar 2024 12:55:40 -0700 Subject: [PATCH 061/159] Suppress warnings for remote Clang targets This mirrors behavior of remote swift targets which suppress warnings as well. --- .../BuildDescription/ClangTargetBuildDescription.swift | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift b/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift index 09548888d5c..1387d2933f4 100644 --- a/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift @@ -323,6 +323,15 @@ package final class ClangTargetBuildDescription { args += ["-I", toolchainResourcesPath.pathString] } + // suppress warnings if the package is remote + if self.package.isRemote { + args += ["-w"] + // `-w` (suppress warnings) and `-Werror` (warnings as errors) flags are mutually exclusive + if let index = args.firstIndex(of: "-Werror") { + args.remove(at: index) + } + } + return args } From c4a3af1ae4789ba70f56c2daae3dd111d5d1615d Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 27 Mar 2024 10:25:08 -0700 Subject: [PATCH 062/159] NFC: Add a test-case for `-w` in clang arguments for "remote" packages Remote in this case means local or remote source control or registry. --- .../ClangTargetBuildDescriptionTests.swift | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/Tests/BuildTests/ClangTargetBuildDescriptionTests.swift b/Tests/BuildTests/ClangTargetBuildDescriptionTests.swift index a7c4cbd6fbc..6a80831300c 100644 --- a/Tests/BuildTests/ClangTargetBuildDescriptionTests.swift +++ b/Tests/BuildTests/ClangTargetBuildDescriptionTests.swift @@ -22,6 +22,7 @@ final class ClangTargetBuildDescriptionTests: XCTestCase { func testClangIndexStorePath() throws { let targetDescription = try makeTargetBuildDescription("test") XCTAssertTrue(try targetDescription.basicArguments().contains("-index-store-path")) + XCTAssertFalse(try targetDescription.basicArguments().contains("-w")) } func testSwiftCorelibsFoundationIncludeWorkaround() throws { @@ -45,6 +46,11 @@ final class ClangTargetBuildDescriptionTests: XCTestCase { XCTAssertTrue(try androidDescription.basicArguments().contains("\(androidParameters.toolchain.swiftResourcesPath!)")) } + func testWarningSuppressionForRemotePackages() throws { + let targetDescription = try makeTargetBuildDescription("test-warning-supression", usesSourceControl: true) + XCTAssertTrue(try targetDescription.basicArguments().contains("-w")) + } + private func makeClangTarget() throws -> ClangTarget { try ClangTarget( name: "dummy", @@ -70,14 +76,20 @@ final class ClangTargetBuildDescriptionTests: XCTestCase { } private func makeTargetBuildDescription(_ packageName: String, - buildParameters: BuildParameters? = nil) throws -> ClangTargetBuildDescription { + buildParameters: BuildParameters? = nil, + usesSourceControl: Bool = false) throws -> ClangTargetBuildDescription { let observability = ObservabilitySystem.makeForTesting(verbose: false) - let manifest = Manifest.createRootManifest( - displayName: "dummy", - toolsVersion: .v5, - targets: [try TargetDescription(name: "dummy")] - ) + let manifest: Manifest + if usesSourceControl { + manifest = Manifest.createLocalSourceControlManifest( + displayName: packageName, path: AbsolutePath("/\(packageName)")) + } else { + manifest = Manifest.createRootManifest( + displayName: packageName, + toolsVersion: .v5, + targets: [try TargetDescription(name: "dummy")]) + } let target = try makeResolvedTarget() From f529daef40418e143ed501ff245109e061db190a Mon Sep 17 00:00:00 2001 From: Egor Zhdan Date: Thu, 28 Mar 2024 13:08:21 +0000 Subject: [PATCH 063/159] [cxx-interop] Propagate interop flag to the test entry point target ### Motivation: Projects that use C++ interop for some of the Swift targets were failing to build in test mode (`swift test`) because the generated test entry point target wasn't being compiled with C++ interop enabled. ### Modifications: The logic that propagates the C++ interop compiler flag (`-cxx-interoperability-mode`) to the discovery target now also runs for the entry point target. rdar://125498997 --- .../Build/BuildDescription/SwiftTargetBuildDescription.swift | 4 ++-- Tests/BuildTests/BuildPlanTests.swift | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift index cbc5bed2f83..cbb9a1d446b 100644 --- a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift @@ -555,11 +555,11 @@ package final class SwiftTargetBuildDescription { args += ["-color-diagnostics"] } - // If this is a generated test discovery target, it might import a test + // If this is a generated test discovery target or a test entry point, it might import a test // target that is built with C++ interop enabled. In that case, the test // discovery target must enable C++ interop as well switch testTargetRole { - case .discovery: + case .discovery, .entryPoint: for dependency in try self.target.recursiveTargetDependencies() { let dependencyScope = self.buildParameters.createScope(for: dependency) let dependencySwiftFlags = dependencyScope.evaluate(.OTHER_SWIFT_FLAGS) diff --git a/Tests/BuildTests/BuildPlanTests.swift b/Tests/BuildTests/BuildPlanTests.swift index bace9c92a9d..a68d4429e4d 100644 --- a/Tests/BuildTests/BuildPlanTests.swift +++ b/Tests/BuildTests/BuildPlanTests.swift @@ -4063,6 +4063,9 @@ final class BuildPlanTests: XCTestCase { let testDiscovery = try result.target(for: "APackageDiscoveredTests").swiftTarget().compileArguments() XCTAssertMatch(testDiscovery, [.anySequence, "-cxx-interoperability-mode=default", "-Xcc", "-std=c++17"]) + + let testEntryPoint = try result.target(for: "APackageTests").swiftTarget().compileArguments() + XCTAssertMatch(testEntryPoint, [.anySequence, "-cxx-interoperability-mode=default", "-Xcc", "-std=c++17"]) } // omit frame pointers explicitly set to true From 8945865a1e15630e8bb3725684ecba52aa749853 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 28 Mar 2024 11:45:57 -0700 Subject: [PATCH 064/159] Allow `swift-corelibs-foundation` to use unsafeFlags when used as a dependency Tagged dependencies don't currently allow use of `unsafeFlags` but we need to temporarily lift that requirement for `swift-corelibs-foundation` that depends on C flags. In the future this would be removed in favor of a more general mechanism for "safe" flags. --- Sources/Workspace/Workspace+Manifests.swift | 11 +++++- Tests/WorkspaceTests/WorkspaceTests.swift | 42 +++++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/Sources/Workspace/Workspace+Manifests.swift b/Sources/Workspace/Workspace+Manifests.swift index 64646669616..e202a624e51 100644 --- a/Sources/Workspace/Workspace+Manifests.swift +++ b/Sources/Workspace/Workspace+Manifests.swift @@ -141,9 +141,16 @@ extension Workspace { let dependency = dependency.dependency switch dependency.state { case .sourceControlCheckout(let checkout): - if checkout.isBranchOrRevisionBased { - result.insert(dependency.packageRef) + let packageRef = dependency.packageRef + + if checkout.isBranchOrRevisionBased + // FIXME: Remove this once we have a general mechanism + // for passing "safe" flags. + || packageRef.identity == .plain("swift-corelibs-foundation") + { + result.insert(packageRef) } + case .registryDownload, .edited, .custom: continue case .fileSystem: diff --git a/Tests/WorkspaceTests/WorkspaceTests.swift b/Tests/WorkspaceTests/WorkspaceTests.swift index 4869739b0c0..783329c01f1 100644 --- a/Tests/WorkspaceTests/WorkspaceTests.swift +++ b/Tests/WorkspaceTests/WorkspaceTests.swift @@ -5590,6 +5590,48 @@ final class WorkspaceTests: XCTestCase { } } + func testUnsafeFlagsInFoundation() throws { + let sandbox = AbsolutePath("/tmp/ws/") + let fs = InMemoryFileSystem() + + let workspace = try MockWorkspace( + sandbox: sandbox, + fileSystem: fs, + roots: [ + MockPackage( + name: "Test", + targets: [ + MockTarget(name: "Test", + dependencies: [ + .product(name: "Foundation", + package: "swift-corelibs-foundation") + ]), + ], + products: [], + dependencies: [ + .sourceControl(path: "swift-corelibs-foundation", requirement: .upToNextMajor(from: "1.0.0")), + ] + ), + ], + packages: [ + MockPackage( + name: "swift-corelibs-foundation", + targets: [ + MockTarget(name: "Foundation", settings: [.init(tool: .swift, kind: .unsafeFlags(["-F", "/tmp"]))]), + ], + products: [ + MockProduct(name: "Foundation", targets: ["Foundation"]) + ], + versions: ["1.0.0", nil] + ) + ] + ) + + try workspace.checkPackageGraph(roots: ["Test"]) { _, diagnostics in + XCTAssertNoDiagnostics(diagnostics) + } + } + func testEditDependencyHadOverridableConstraints() throws { let sandbox = AbsolutePath("/tmp/ws/") let fs = InMemoryFileSystem() From 9386170feaf054d9f527f8de3f0fe9673279395f Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Sat, 30 Mar 2024 18:13:56 +0900 Subject: [PATCH 065/159] Make `supportedTriples` optional in artifactbundle metadata (#7432) Make `supportedTriples` optional in artifactbundle metadata ### Motivation: We don't have a good way to express a Swift SDK that can be used on any host platform except for listing up all possible platforms. ### Modifications: This change allows `supportedTriples` field to be null or missing to express that the SDK is universally usable. Also this fixes a minor issue around `swift experimental-sdk configuration`, that did not take care of runtime compatibility of OS version in host triples. ### Result: SwiftPM now accepts host-platform independent Swift SDK --- .../ArtifactsArchiveMetadata.swift | 6 +- .../SwiftSDKs/SwiftSDKBundle.swift | 21 +++++-- .../BinaryTarget+Extensions.swift | 5 +- Tests/PackageModelTests/SwiftSDKTests.swift | 61 +++++++++++++++++++ .../ArtifactsArchiveMetadataTests.swift | 57 +++++++++++++++++ 5 files changed, 140 insertions(+), 10 deletions(-) diff --git a/Sources/PackageModel/ArtifactsArchiveMetadata.swift b/Sources/PackageModel/ArtifactsArchiveMetadata.swift index 6c10ac564aa..b76333ae4a0 100644 --- a/Sources/PackageModel/ArtifactsArchiveMetadata.swift +++ b/Sources/PackageModel/ArtifactsArchiveMetadata.swift @@ -52,9 +52,9 @@ public struct ArtifactsArchiveMetadata: Equatable { public struct Variant: Equatable { public let path: RelativePath - public let supportedTriples: [Triple] + public let supportedTriples: [Triple]? - public init(path: RelativePath, supportedTriples: [Triple]) { + public init(path: RelativePath, supportedTriples: [Triple]?) { self.path = path self.supportedTriples = supportedTriples } @@ -121,7 +121,7 @@ extension ArtifactsArchiveMetadata.Variant: Decodable { public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - self.supportedTriples = try container.decode([String].self, forKey: .supportedTriples).map { try Triple($0) } + self.supportedTriples = try container.decodeIfPresent([String].self, forKey: .supportedTriples)?.map { try Triple($0) } self.path = try RelativePath(validating: container.decode(String.self, forKey: .path)) } } diff --git a/Sources/PackageModel/SwiftSDKs/SwiftSDKBundle.swift b/Sources/PackageModel/SwiftSDKs/SwiftSDKBundle.swift index 8b743666244..755c4153845 100644 --- a/Sources/PackageModel/SwiftSDKs/SwiftSDKBundle.swift +++ b/Sources/PackageModel/SwiftSDKs/SwiftSDKBundle.swift @@ -33,6 +33,19 @@ public struct SwiftSDKBundle { public var name: String { path.basename } } +extension SwiftSDKBundle.Variant { + /// Whether the given host triple is supported by this SDK variant + internal func isSupporting(hostTriple: Triple) -> Bool { + guard let supportedTriples = metadata.supportedTriples else { + // No supportedTriples means the SDK can be universally usable + return true + } + return supportedTriples.contains(where: { variantTriple in + hostTriple.isRuntimeCompatible(with: variantTriple) + }) + } +} + extension [SwiftSDKBundle] { /// Select a Swift SDK with a given artifact ID from a `self` array of available Swift SDKs. /// - Parameters: @@ -48,7 +61,7 @@ extension [SwiftSDKBundle] { } for variant in variants { - guard variant.metadata.supportedTriples.contains(hostTriple) else { + guard variant.isSupporting(hostTriple: hostTriple) else { continue } @@ -77,11 +90,7 @@ extension [SwiftSDKBundle] { for bundle in self { for (artifactID, variants) in bundle.artifacts { for variant in variants { - guard variant.metadata.supportedTriples.contains(where: { variantTriple in - hostTriple.isRuntimeCompatible(with: variantTriple) - }) else { - continue - } + guard variant.isSupporting(hostTriple: hostTriple) else { continue } for swiftSDK in variant.swiftSDKs { if artifactID == selector { diff --git a/Sources/SPMBuildCore/BinaryTarget+Extensions.swift b/Sources/SPMBuildCore/BinaryTarget+Extensions.swift index 278c3b0167e..64f398394d6 100644 --- a/Sources/SPMBuildCore/BinaryTarget+Extensions.swift +++ b/Sources/SPMBuildCore/BinaryTarget+Extensions.swift @@ -69,7 +69,10 @@ extension BinaryTarget { // Filter supported triples with versionLessTriple and pass into // ExecutableInfo; empty if non matching triples found. try entry.value.variants.map { - let filteredSupportedTriples = try $0.supportedTriples + guard let supportedTriples = $0.supportedTriples else { + throw StringError("No \"supportedTriples\" found in the artifact metadata for \(entry.key) in \(self.artifactPath)") + } + let filteredSupportedTriples = try supportedTriples .filter { try $0.withoutVersion() == versionLessTriple } return ExecutableInfo( name: entry.key, diff --git a/Tests/PackageModelTests/SwiftSDKTests.swift b/Tests/PackageModelTests/SwiftSDKTests.swift index 889906b76a5..a5b27fdfba6 100644 --- a/Tests/PackageModelTests/SwiftSDKTests.swift +++ b/Tests/PackageModelTests/SwiftSDKTests.swift @@ -21,6 +21,7 @@ private let bundleRootPath = try! AbsolutePath(validating: "/tmp/cross-toolchain private let toolchainBinDir = RelativePath("swift.xctoolchain/usr/bin") private let sdkRootDir = RelativePath("ubuntu-jammy.sdk") private let hostTriple = try! Triple("arm64-apple-darwin22.1.0") +private let olderHostTriple = try! Triple("arm64-apple-darwin20.1.0") private let linuxGNUTargetTriple = try! Triple("x86_64-unknown-linux-gnu") private let linuxMuslTargetTriple = try! Triple("x86_64-unknown-linux-musl") private let extraFlags = BuildFlags( @@ -291,6 +292,12 @@ private let parsedDestinationV2Musl = SwiftSDK( pathsConfiguration: .init(sdkRootPath: sdkRootAbsolutePath) ) +private let parsedDestinationForOlderHost = SwiftSDK( + targetTriple: linuxMuslTargetTriple, + toolset: .init(toolchainBinDir: toolchainBinAbsolutePath, buildFlags: extraFlags), + pathsConfiguration: .init(sdkRootPath: sdkRootAbsolutePath) +) + private let parsedToolsetNoRootDestination = SwiftSDK( targetTriple: linuxGNUTargetTriple, toolset: .init( @@ -520,6 +527,24 @@ final class DestinationTests: XCTestCase { swiftSDKs: [parsedDestinationV2Musl] ), ], + "id4": [ + .init( + metadata: .init( + path: "id4", + supportedTriples: [olderHostTriple] + ), + swiftSDKs: [parsedDestinationForOlderHost] + ), + ], + "id5": [ + .init( + metadata: .init( + path: "id5", + supportedTriples: nil + ), + swiftSDKs: [parsedDestinationV2GNU] + ), + ], ] ), ] @@ -553,5 +578,41 @@ final class DestinationTests: XCTestCase { ), parsedDestinationV2Musl ) + + // Newer hostTriple should match with older supportedTriples + XCTAssertEqual( + bundles.selectSwiftSDK( + id: "id4", + hostTriple: hostTriple, + targetTriple: linuxMuslTargetTriple + ), + parsedDestinationForOlderHost + ) + XCTAssertEqual( + bundles.selectSwiftSDK( + matching: "id4", + hostTriple: hostTriple, + observabilityScope: system.topScope + ), + parsedDestinationForOlderHost + ) + + // nil supportedTriples should match with any hostTriple + XCTAssertEqual( + bundles.selectSwiftSDK( + id: "id5", + hostTriple: hostTriple, + targetTriple: linuxGNUTargetTriple + ), + parsedDestinationV2GNU + ) + XCTAssertEqual( + bundles.selectSwiftSDK( + matching: "id5", + hostTriple: hostTriple, + observabilityScope: system.topScope + ), + parsedDestinationV2GNU + ) } } diff --git a/Tests/SPMBuildCoreTests/ArtifactsArchiveMetadataTests.swift b/Tests/SPMBuildCoreTests/ArtifactsArchiveMetadataTests.swift index 4e3690ef2f4..2ab3ce8cdb4 100644 --- a/Tests/SPMBuildCoreTests/ArtifactsArchiveMetadataTests.swift +++ b/Tests/SPMBuildCoreTests/ArtifactsArchiveMetadataTests.swift @@ -65,4 +65,61 @@ final class ArtifactsArchiveMetadataTests: XCTestCase { ] )) } + func testParseMetadataWithoutSupportedTriple() throws { + let fileSystem = InMemoryFileSystem() + try fileSystem.writeFileContents( + "/info.json", + string: """ + { + "schemaVersion": "1.0", + "artifacts": { + "protocol-buffer-compiler": { + "type": "executable", + "version": "3.5.1", + "variants": [ + { + "path": "x86_64-apple-macosx/protoc" + }, + { + "path": "x86_64-unknown-linux-gnu/protoc", + "supportedTriples": null + } + ] + } + } + } + """ + ) + + let metadata = try ArtifactsArchiveMetadata.parse(fileSystem: fileSystem, rootPath: .root) + XCTAssertEqual(metadata, ArtifactsArchiveMetadata( + schemaVersion: "1.0", + artifacts: [ + "protocol-buffer-compiler": ArtifactsArchiveMetadata.Artifact( + type: .executable, + version: "3.5.1", + variants: [ + ArtifactsArchiveMetadata.Variant( + path: "x86_64-apple-macosx/protoc", + supportedTriples: nil + ), + ArtifactsArchiveMetadata.Variant( + path: "x86_64-unknown-linux-gnu/protoc", + supportedTriples: nil + ), + ] + ), + ] + )) + + let binaryTarget = BinaryTarget( + name: "protoc", kind: .artifactsArchive, path: .root, origin: .local + ) + // No supportedTriples with binaryTarget should be rejected + XCTAssertThrowsError( + try binaryTarget.parseArtifactArchives( + for: Triple("x86_64-apple-macosx"), fileSystem: fileSystem + ) + ) + } } From ea1730b6dca9e3460a068a177f062d23f5e2b464 Mon Sep 17 00:00:00 2001 From: k-kohey Date: Sat, 30 Mar 2024 22:57:02 +0900 Subject: [PATCH 066/159] Improve error message about the PackageGraphError.productDependencyNotFound (#7419) Fixed a bug in the productDependencyNotFound error message ### Motivation: fix https://github.com/apple/swift-package-manager/issues/7398 The above issue suggests the following two defects. 1. package dependency resolution changes depending on the order of packages (alphabetical order) 2. the phrase "Did you meen..." in the error message in the error message is not on target. ### Modifications: The first problem is as described in the issue, if users rename the directory containing Package.swift from repro to zzz, the "Did you meen..." will appear. will appear. Essentially, the error minutes should be displayed in full, regardless of the name of the directory. This is due to the fact that the loop used to resolve dependencies depends on the alphabetical order of the directories and the side effect on allTargetName inside the loop. Therefore, the side effect for allTargetName inside the loop has been moved to the outside of the loop. The second problem is that when dependency A of a package is not found, a strange suggestion "Did you meen `A`? ", which is a strange suggestion. This is because when a dependency is not found, the message "Did you meen ``" is printed if there is a dependency with a similar name (even the exact same name). Thus, if the names are the same, "Did you meen `.product(name: ... , package: "swift-argugument")"`" instead of the found target name. ### Result: Command to execute: `swift build` Result of command:. `error: 'repro': product 'ArgumentParser' required by package 'repro' target 'repro' not found. Did you mean '.product(name: "ArgumentParser", package: "swift-argument-parser")'?` Condition: The following steps were taken to create the environment. 1. mkdir repro 1. cd repro 1. swift package init --type executable 1. Open Package.swift and make sure it has this content: ```swift // swift-tools-version: 5.10 import PackageDescription let package = Package( name: "repro", dependencies: [ .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.3.0") ], targets: [ .executableTarget( name: "repro", dependencies: ["ArgumentParser"] ) ] ) ``` --------- Co-authored-by: Yuta Saito --- .../PackageGraph/ModulesGraph+Loading.swift | 23 +++++++-- Sources/PackageGraph/ModulesGraph.swift | 8 +-- .../PackageGraphTests/ModulesGraphTests.swift | 51 +++++++++++++++++++ 3 files changed, 74 insertions(+), 8 deletions(-) diff --git a/Sources/PackageGraph/ModulesGraph+Loading.swift b/Sources/PackageGraph/ModulesGraph+Loading.swift index a056875684a..5c0ab4e4ea1 100644 --- a/Sources/PackageGraph/ModulesGraph+Loading.swift +++ b/Sources/PackageGraph/ModulesGraph+Loading.swift @@ -434,6 +434,13 @@ private func createResolvedPackages( // Track if multiple targets are found with the same name. var foundDuplicateTarget = false + for packageBuilder in packageBuilders { + for target in packageBuilder.targets { + // Record if we see a duplicate target. + foundDuplicateTarget = foundDuplicateTarget || !allTargetNames.insert(target.target.name).inserted + } + } + // Do another pass and establish product dependencies of each target. for packageBuilder in packageBuilders { let package = packageBuilder.package @@ -493,9 +500,6 @@ private func createResolvedPackages( // Establish dependencies in each target. for targetBuilder in packageBuilder.targets { - // Record if we see a duplicate target. - foundDuplicateTarget = foundDuplicateTarget || !allTargetNames.insert(targetBuilder.target.name).inserted - // Directly add all the system module dependencies. targetBuilder.dependencies += implicitSystemTargetDeps.map { .target($0, conditions: []) } @@ -524,13 +528,22 @@ private func createResolvedPackages( } else { // Find a product name from the available product dependencies that is most similar to the required product name. let bestMatchedProductName = bestMatch(for: productRef.name, from: Array(allTargetNames)) + var packageContainingBestMatchedProduct: String? + if let bestMatchedProductName, productRef.name == bestMatchedProductName { + let dependentPackages = packageBuilder.dependencies.map(\.package) + for p in dependentPackages where p.targets.contains(where: { $0.name == bestMatchedProductName }) { + packageContainingBestMatchedProduct = p.identity.description + break + } + } let error = PackageGraphError.productDependencyNotFound( package: package.identity.description, targetName: targetBuilder.target.name, dependencyProductName: productRef.name, dependencyPackageName: productRef.package, dependencyProductInDecl: !declProductsAsDependency.isEmpty, - similarProductName: bestMatchedProductName + similarProductName: bestMatchedProductName, + packageContainingSimilarProduct: packageContainingBestMatchedProduct ) packageObservabilityScope.emit(error) } @@ -568,7 +581,7 @@ private func createResolvedPackages( // If a target with similar name was encountered before, we emit a diagnostic. if foundDuplicateTarget { var duplicateTargets = [String: [Package]]() - for targetName in allTargetNames.sorted() { + for targetName in Set(allTargetNames).sorted() { let packages = packageBuilders .filter({ $0.targets.contains(where: { $0.target.name == targetName }) }) .map{ $0.package } diff --git a/Sources/PackageGraph/ModulesGraph.swift b/Sources/PackageGraph/ModulesGraph.swift index 471d6192e5e..84f1bcd4907 100644 --- a/Sources/PackageGraph/ModulesGraph.swift +++ b/Sources/PackageGraph/ModulesGraph.swift @@ -23,7 +23,7 @@ enum PackageGraphError: Swift.Error { case cycleDetected((path: [Manifest], cycle: [Manifest])) /// The product dependency not found. - case productDependencyNotFound(package: String, targetName: String, dependencyProductName: String, dependencyPackageName: String?, dependencyProductInDecl: Bool, similarProductName: String?) + case productDependencyNotFound(package: String, targetName: String, dependencyProductName: String, dependencyPackageName: String?, dependencyProductInDecl: Bool, similarProductName: String?, packageContainingSimilarProduct: String?) /// The package dependency already satisfied by a different dependency package case dependencyAlreadySatisfiedByIdentifier(package: String, dependencyLocation: String, otherDependencyURL: String, identity: PackageIdentity) @@ -230,12 +230,14 @@ extension PackageGraphError: CustomStringConvertible { (cycle.path + cycle.cycle).map({ $0.displayName }).joined(separator: " -> ") + " -> " + cycle.cycle[0].displayName - case .productDependencyNotFound(let package, let targetName, let dependencyProductName, let dependencyPackageName, let dependencyProductInDecl, let similarProductName): + case .productDependencyNotFound(let package, let targetName, let dependencyProductName, let dependencyPackageName, let dependencyProductInDecl, let similarProductName, let packageContainingSimilarProduct): if dependencyProductInDecl { return "product '\(dependencyProductName)' is declared in the same package '\(package)' and can't be used as a dependency for target '\(targetName)'." } else { var description = "product '\(dependencyProductName)' required by package '\(package)' target '\(targetName)' \(dependencyPackageName.map{ "not found in package '\($0)'" } ?? "not found")." - if let similarProductName { + if let similarProductName, let packageContainingSimilarProduct { + description += " Did you mean '.product(name: \"\(similarProductName)\", package: \"\(packageContainingSimilarProduct)\")'?" + } else if let similarProductName { description += " Did you mean '\(similarProductName)'?" } return description diff --git a/Tests/PackageGraphTests/ModulesGraphTests.swift b/Tests/PackageGraphTests/ModulesGraphTests.swift index efae57d71a4..420387d5d7d 100644 --- a/Tests/PackageGraphTests/ModulesGraphTests.swift +++ b/Tests/PackageGraphTests/ModulesGraphTests.swift @@ -2676,6 +2676,57 @@ final class ModulesGraphTests: XCTestCase { XCTAssertEqual(observability.diagnostics.count, 0, "unexpected diagnostics: \(observability.diagnostics.map { $0.description })") } + + func testDependencyResolutionWithErrorMessages() throws { + let fs = InMemoryFileSystem(emptyFiles: + "/aaa/Sources/aaa/main.swift", + "/zzz/Sources/zzz/source.swift" + ) + + let observability = ObservabilitySystem.makeForTesting() + let _ = try loadModulesGraph( + fileSystem: fs, + manifests: [ + Manifest.createRootManifest( + displayName: "aaa", + path: "/aaa", + dependencies: [ + .localSourceControl(path: "/zzz", requirement: .upToNextMajor(from: "1.0.0")) + ], + products: [], + targets: [ + TargetDescription( + name: "aaa", + dependencies: ["zzy"], + type: .executable + ) + ]), + Manifest.createRootManifest( + displayName: "zzz", + path: "/zzz", + products: [ + ProductDescription( + name: "zzz", + type: .library(.automatic), + targets: ["zzz"] + ) + ], + targets: [ + TargetDescription( + name: "zzz" + ) + ]) + ], + observabilityScope: observability.topScope + ) + + testDiagnostics(observability.diagnostics) { result in + result.check( + diagnostic: "product 'zzy' required by package 'aaa' target 'aaa' not found. Did you mean 'zzz'?", + severity: .error + ) + } + } } From 0246381c48680d19cdc0790f9ca0487de7ae9eec Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 29 Mar 2024 13:24:10 -0700 Subject: [PATCH 067/159] UserToolchain: Unify handling of confg.json and provided-libraries.json --- Sources/PackageModel/UserToolchain.swift | 58 ++++++++++++------------ 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/Sources/PackageModel/UserToolchain.swift b/Sources/PackageModel/UserToolchain.swift index 588e238ab3d..9b7042332bb 100644 --- a/Sources/PackageModel/UserToolchain.swift +++ b/Sources/PackageModel/UserToolchain.swift @@ -533,40 +533,25 @@ public final class UserToolchain: Toolchain { if let customInstalledSwiftPMConfiguration { self.installedSwiftPMConfiguration = customInstalledSwiftPMConfiguration } else { - let path = self.swiftCompilerPath.parentDirectory.parentDirectory.appending(components: ["share", "pm", "config.json"]) - if localFileSystem.exists(path) { - self.installedSwiftPMConfiguration = try JSONDecoder.makeWithDefaults().decode(path: path, fileSystem: localFileSystem, as: InstalledSwiftPMConfiguration.self) - } else { - // We *could* eventually make this an error, but not for a few releases. - self.installedSwiftPMConfiguration = InstalledSwiftPMConfiguration.default - } + let path = swiftCompilerPath.parentDirectory.parentDirectory.appending(components: [ + "share", "pm", "config.json", + ]) + self.installedSwiftPMConfiguration = try Self.loadJSONResource( + config: path, + type: InstalledSwiftPMConfiguration.self, + default: InstalledSwiftPMConfiguration.default) } if let customProvidedLibraries { self.providedLibraries = customProvidedLibraries } else { - // When building with CMake or `swift build --build-system xcode`, we need to skip resource support. - #if SKIP_RESOURCE_SUPPORT - let path = self.swiftCompilerPath.parentDirectory.parentDirectory.appending(components: ["share", "pm", "provided-libraries.json"]) - #else - let path: AbsolutePath - if let developmentPath = Bundle.module.path(forResource: "provided-libraries", ofType: "json") { - // During development, we should be able to find the metadata file using `Bundle.module`. - path = try AbsolutePath(validating: developmentPath) - } else { - // When deployed, we can find the metadata file in the toolchain. - path = self.swiftCompilerPath.parentDirectory.parentDirectory.appending(components: ["share", "pm", "provided-libraries.json"]) - } - #endif - if localFileSystem.exists(path) { - self.providedLibraries = try JSONDecoder.makeWithDefaults().decode( - path: path, - fileSystem: localFileSystem, - as: [LibraryMetadata].self - ) - } else { - self.providedLibraries = [] - } + let path = swiftCompilerPath.parentDirectory.parentDirectory.appending(components: [ + "share", "pm", "provided-libraries.json", + ]) + self.providedLibraries = try Self.loadJSONResource( + config: path, + type: [LibraryMetadata].self, + default: []) } // Use the triple from Swift SDK or compute the host triple using swiftc. @@ -895,4 +880,19 @@ public final class UserToolchain: Toolchain { } } } + + private static func loadJSONResource( + config: AbsolutePath, type: T.Type, `default`: T + ) + throws -> T + { + if localFileSystem.exists(config) { + return try JSONDecoder.makeWithDefaults().decode( + path: config, + fileSystem: localFileSystem, + as: type) + } + + return `default` + } } From 26b4ed010e7ec211f0aad7527961f83508ea4771 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 29 Mar 2024 13:25:23 -0700 Subject: [PATCH 068/159] Utilities: Install provided-libraries.json --- Utilities/bootstrap | 3 +++ .../provided-libraries.json | 0 2 files changed, 3 insertions(+) rename {Sources/PackageModel/InstalledLibrariesSupport => Utilities}/provided-libraries.json (100%) diff --git a/Utilities/bootstrap b/Utilities/bootstrap index 76923ddac92..2da5c06d725 100755 --- a/Utilities/bootstrap +++ b/Utilities/bootstrap @@ -403,6 +403,9 @@ def install(args): config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "config.json") install_file(args, config_path, os.path.join(os.path.join(prefix, "share"), "pm")) + libs_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "provided-libraries.json") + install_file(args, config_path, os.path.join(os.path.join(prefix, "share"), "pm")) + # Install libSwiftPM if an install directory was provided. if args.libswiftpm_install_dir: libswiftpm_modules = [ diff --git a/Sources/PackageModel/InstalledLibrariesSupport/provided-libraries.json b/Utilities/provided-libraries.json similarity index 100% rename from Sources/PackageModel/InstalledLibrariesSupport/provided-libraries.json rename to Utilities/provided-libraries.json From fc8d9b389d23e3d67ba1f8edd88bced08e6e8bb6 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 2 Apr 2024 21:38:34 +0100 Subject: [PATCH 069/159] Delete `Examples/package-info` directory as outdated (#7434) This example package was created many years ago during an initial attempt of versioning SwiftPM with semver. That approach has been abandoned since then, and SwiftPM doesn't provide any stable API outside of `PackageDescription` and `PackagePlugin` modules. This example code can mislead users of SwiftPM to believe that it can be consumed as a library with a stable API in other modules, which is currently not the case. --- Examples/package-info/Package.swift | 25 ---------- Examples/package-info/README.md | 3 -- .../Sources/package-info/example.swift | 49 ------------------- Package.swift | 6 --- 4 files changed, 83 deletions(-) delete mode 100644 Examples/package-info/Package.swift delete mode 100644 Examples/package-info/README.md delete mode 100644 Examples/package-info/Sources/package-info/example.swift diff --git a/Examples/package-info/Package.swift b/Examples/package-info/Package.swift deleted file mode 100644 index 94e5d6d6ffa..00000000000 --- a/Examples/package-info/Package.swift +++ /dev/null @@ -1,25 +0,0 @@ -// swift-tools-version:5.5 - -import PackageDescription - -let package = Package( - name: "package-info", - platforms: [ - .macOS(.v12), - .iOS(.v13) - ], - dependencies: [ - // This just points to the SwiftPM at the root of this repository. - .package(name: "swift-package-manager", path: "../../"), - // You will want to depend on a stable semantic version instead: - // .package(url: "https://github.com/apple/swift-package-manager", .exact("0.4.0")) - ], - targets: [ - .executableTarget( - name: "package-info", - dependencies: [ - .product(name: "SwiftPM", package: "swift-package-manager") - ] - ), - ] -) diff --git a/Examples/package-info/README.md b/Examples/package-info/README.md deleted file mode 100644 index 8d316f30a44..00000000000 --- a/Examples/package-info/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# package-info - -Sample package built on top of libSwiftPM. diff --git a/Examples/package-info/Sources/package-info/example.swift b/Examples/package-info/Sources/package-info/example.swift deleted file mode 100644 index 0ec45d4ecf5..00000000000 --- a/Examples/package-info/Sources/package-info/example.swift +++ /dev/null @@ -1,49 +0,0 @@ -import Basics -import Workspace - -@main -@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *) -struct Example { - static func main() async throws { - // PREREQUISITES - // ============ - - // We need a package to work with. - // This computes the path of this package root based on the file location - let packagePath = try AbsolutePath(validating: #file).parentDirectory.parentDirectory.parentDirectory - - // LOADING - // ======= - - // There are several levels of information available. - // Each takes longer to load than the level above it, but provides more detail. - - let observability = ObservabilitySystem({ print("\($0): \($1)") }) - - let workspace = try Workspace(forRootPackage: packagePath) - - let manifest = try await workspace.loadRootManifest(at: packagePath, observabilityScope: observability.topScope) - - let package = try await workspace.loadRootPackage(at: packagePath, observabilityScope: observability.topScope) - - let graph = try workspace.loadPackageGraph(rootPath: packagePath, observabilityScope: observability.topScope) - - // EXAMPLES - // ======== - - // Manifest - let products = manifest.products.map({ $0.name }).joined(separator: ", ") - print("Products:", products) - - let targets = manifest.targets.map({ $0.name }).joined(separator: ", ") - print("Targets:", targets) - - // Package - let executables = package.targets.filter({ $0.type == .executable }).map({ $0.name }) - print("Executable targets:", executables) - - // PackageGraph - let numberOfFiles = graph.reachableTargets.reduce(0, { $0 + $1.sources.paths.count }) - print("Total number of source files (including dependencies):", numberOfFiles) - } -} diff --git a/Package.swift b/Package.swift index 80269ac3309..5ca57fece39 100644 --- a/Package.swift +++ b/Package.swift @@ -664,12 +664,6 @@ let package = Package( name: "XCBuildSupportTests", dependencies: ["XCBuildSupport", "SPMTestSupport"], exclude: ["Inputs/Foo.pc"] - ), - // Examples (These are built to ensure they stay up to date with the API.) - .executableTarget( - name: "package-info", - dependencies: ["Workspace"], - path: "Examples/package-info/Sources/package-info" ) ], swiftLanguageVersions: [.v5] From 8193ed6b452cfc7e108911a20803cdb36446dd0e Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Wed, 3 Apr 2024 20:48:24 +0100 Subject: [PATCH 070/159] Update Swift version to 5.9 in `.swiftformat` (#7438) We require Swift 5.9 to build the package, so we can adopt 5.9-specific formatting tweak as well. --- .swiftformat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.swiftformat b/.swiftformat index 8ae09674179..4ee24410ab8 100644 --- a/.swiftformat +++ b/.swiftformat @@ -1,6 +1,6 @@ ## File options ---swiftversion 5.7 +--swiftversion 5.9 --exclude .build ## Formatting options From f4ab9a43f3cfbb8f184043435f925b67b0070f36 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 4 Apr 2024 02:25:11 -0700 Subject: [PATCH 071/159] [PubGrub] Avoid resolving package versions twice in presence of prebuilt libraries (#7436) Integrate information about versions of prebuilt libraries into the solver and prioritize these versions when making decisions if they meet all of the requirements of undecided terms. This helps solver to avoid fetching and at the same time allows partial matches on prebuilt libraries with fallback to package provided versions. --- Sources/PackageGraph/CMakeLists.txt | 1 - .../PrebuiltPackageContainer.swift | 69 - .../PubGrub/ContainerProvider.swift | 35 +- .../PubGrub/PubGrubDependencyResolver.swift | 229 ++- Sources/SPMTestSupport/Resolver.swift | 19 - .../ResolverPrecomputationProvider.swift | 58 +- .../Workspace/Workspace+Dependencies.swift | 46 +- Tests/PackageGraphTests/PubgrubTests.swift | 1480 +++++++++++------ 8 files changed, 1157 insertions(+), 780 deletions(-) delete mode 100644 Sources/PackageGraph/PrebuiltPackageContainer.swift delete mode 100644 Sources/SPMTestSupport/Resolver.swift diff --git a/Sources/PackageGraph/CMakeLists.txt b/Sources/PackageGraph/CMakeLists.txt index c0910900ec9..6a1695a0a4d 100644 --- a/Sources/PackageGraph/CMakeLists.txt +++ b/Sources/PackageGraph/CMakeLists.txt @@ -19,7 +19,6 @@ add_library(PackageGraph PackageGraphRoot.swift PackageModel+Extensions.swift PackageRequirement.swift - PrebuiltPackageContainer.swift PinsStore.swift Resolution/PubGrub/Assignment.swift Resolution/PubGrub/ContainerProvider.swift diff --git a/Sources/PackageGraph/PrebuiltPackageContainer.swift b/Sources/PackageGraph/PrebuiltPackageContainer.swift deleted file mode 100644 index 870487d1781..00000000000 --- a/Sources/PackageGraph/PrebuiltPackageContainer.swift +++ /dev/null @@ -1,69 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift open source project -// -// Copyright (c) 2024 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See http://swift.org/LICENSE.txt for license information -// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import Basics -import PackageModel -import struct TSCUtility.Version - -/// A package container that can represent a prebuilt library from a package. -public struct PrebuiltPackageContainer: PackageContainer { - private let chosenIdentity: LibraryMetadata.Identity - private let metadata: LibraryMetadata - - public init(metadata: LibraryMetadata) throws { - self.metadata = metadata - - // FIXME: Unclear what is supposed to happen if we have multiple identities. - if let identity = metadata.identities.first { - self.chosenIdentity = identity - } else { - let name = metadata.productName.map { "'\($0)' " } ?? "" - throw InternalError("provided library \(name)does not specifiy any identities") - } - } - - public var package: PackageReference { - return .init(identity: chosenIdentity.identity, kind: chosenIdentity.kind) - } - - public func isToolsVersionCompatible(at version: Version) -> Bool { - return true - } - - public func toolsVersion(for version: Version) throws -> ToolsVersion { - return .v4 - } - - public func toolsVersionsAppropriateVersionsDescending() throws -> [Version] { - return try versionsAscending() - } - - public func versionsAscending() throws -> [Version] { - return [.init(stringLiteral: metadata.version)] - } - - public func getDependencies(at version: Version, productFilter: ProductFilter) throws -> [PackageContainerConstraint] { - return [] - } - - public func getDependencies(at revision: String, productFilter: ProductFilter) throws -> [PackageContainerConstraint] { - return [] - } - - public func getUnversionedDependencies(productFilter: ProductFilter) throws -> [PackageContainerConstraint] { - return [] - } - - public func loadPackageReference(at boundVersion: BoundVersion) throws -> PackageReference { - return package - } -} diff --git a/Sources/PackageGraph/Resolution/PubGrub/ContainerProvider.swift b/Sources/PackageGraph/Resolution/PubGrub/ContainerProvider.swift index 74735c9ccf9..d73e41ffdf9 100644 --- a/Sources/PackageGraph/Resolution/PubGrub/ContainerProvider.swift +++ b/Sources/PackageGraph/Resolution/PubGrub/ContainerProvider.swift @@ -49,8 +49,8 @@ final class ContainerProvider { } func removeCachedContainers(for packages: [PackageReference]) { - packages.forEach { - self.containersCache[$0] = nil + for package in packages { + self.containersCache[package] = nil } } @@ -65,7 +65,6 @@ final class ContainerProvider { /// Get the container for the given identifier, loading it if necessary. func getContainer( for package: PackageReference, - availableLibraries: [LibraryMetadata], completion: @escaping (Result) -> Void ) { // Return the cached container, if available. @@ -73,17 +72,6 @@ final class ContainerProvider { return completion(.success(container)) } - if let metadata = package.matchingPrebuiltLibrary(in: availableLibraries) { - do { - let prebuiltPackageContainer = try PrebuiltPackageContainer(metadata: metadata) - let pubGrubContainer = PubGrubPackageContainer(underlying: prebuiltPackageContainer, pins: self.pins) - self.containersCache[package] = pubGrubContainer - return completion(.success(pubGrubContainer)) - } catch { - return completion(.failure(error)) - } - } - if let prefetchSync = self.prefetches[package] { // If this container is already being prefetched, wait for that to complete prefetchSync.notify(queue: .sharedConcurrent) { @@ -93,7 +81,7 @@ final class ContainerProvider { } else { // if prefetch failed, remove from list of prefetches and try again self.prefetches[package] = nil - return self.getContainer(for: package, availableLibraries: availableLibraries, completion: completion) + return self.getContainer(for: package, completion: completion) } } } else { @@ -101,7 +89,10 @@ final class ContainerProvider { self.underlying.getContainer( for: package, updateStrategy: self.skipUpdate ? .never : .always, // TODO: make this more elaborate - observabilityScope: self.observabilityScope.makeChildScope(description: "getting package container", metadata: package.diagnosticsMetadata), + observabilityScope: self.observabilityScope.makeChildScope( + description: "getting package container", + metadata: package.diagnosticsMetadata + ), on: .sharedConcurrent ) { result in let result = result.tryMap { container -> PubGrubPackageContainer in @@ -118,7 +109,7 @@ final class ContainerProvider { /// Starts prefetching the given containers. func prefetch(containers identifiers: [PackageReference], availableLibraries: [LibraryMetadata]) { let filteredIdentifiers = identifiers.filter { - return $0.matchingPrebuiltLibrary(in: availableLibraries) == nil + $0.matchingPrebuiltLibrary(in: availableLibraries) == nil } // Process each container. for identifier in filteredIdentifiers { @@ -133,13 +124,19 @@ final class ContainerProvider { self.underlying.getContainer( for: identifier, updateStrategy: self.skipUpdate ? .never : .always, // TODO: make this more elaborate - observabilityScope: self.observabilityScope.makeChildScope(description: "prefetching package container", metadata: identifier.diagnosticsMetadata), + observabilityScope: self.observabilityScope.makeChildScope( + description: "prefetching package container", + metadata: identifier.diagnosticsMetadata + ), on: .sharedConcurrent ) { result in defer { self.prefetches[identifier]?.leave() } // only cache positive results if case .success(let container) = result { - self.containersCache[identifier] = PubGrubPackageContainer(underlying: container, pins: self.pins) + self.containersCache[identifier] = PubGrubPackageContainer( + underlying: container, + pins: self.pins + ) } } } diff --git a/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift b/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift index 2d1993bbcbe..ffee1492d1e 100644 --- a/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift +++ b/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift @@ -25,7 +25,7 @@ public struct PubGrubDependencyResolver { public typealias Constraint = PackageContainerConstraint /// the mutable state that get computed - internal final class State { + final class State { /// The root package reference. let root: DependencyResolutionNode @@ -43,10 +43,11 @@ public struct PubGrubDependencyResolver { private let lock = NSLock() - init(root: DependencyResolutionNode, - overriddenPackages: [PackageReference: (version: BoundVersion, products: ProductFilter)] = [:], - solution: PartialSolution = PartialSolution()) - { + init( + root: DependencyResolutionNode, + overriddenPackages: [PackageReference: (version: BoundVersion, products: ProductFilter)] = [:], + solution: PartialSolution = PartialSolution() + ) { self.root = root self.overriddenPackages = overriddenPackages self.solution = solution @@ -103,6 +104,9 @@ public struct PubGrubDependencyResolver { /// Reference to the pins store, if provided. private let pins: PinsStore.Pins + /// The packages that are available in a prebuilt form in SDK or a toolchain + private let availableLibraries: [LibraryMetadata] + /// The container provider used to load package containers. private let provider: ContainerProvider @@ -121,6 +125,7 @@ public struct PubGrubDependencyResolver { public init( provider: PackageContainerProvider, pins: PinsStore.Pins = [:], + availableLibraries: [LibraryMetadata] = [], skipDependenciesUpdates: Bool = false, prefetchBasedOnResolvedFile: Bool = false, observabilityScope: ObservabilityScope, @@ -128,6 +133,7 @@ public struct PubGrubDependencyResolver { ) { self.packageContainerProvider = provider self.pins = pins + self.availableLibraries = availableLibraries self.skipDependenciesUpdates = skipDependenciesUpdates self.prefetchBasedOnResolvedFile = prefetchBasedOnResolvedFile self.provider = ContainerProvider( @@ -140,11 +146,7 @@ public struct PubGrubDependencyResolver { } /// Execute the resolution algorithm to find a valid assignment of versions. - public func solve(constraints: [Constraint], availableLibraries: [LibraryMetadata], preferPrebuiltLibraries: Bool) -> Result<[DependencyResolverBinding], Error> { - if !preferPrebuiltLibraries { - self.provider.removeCachedContainers(for: availableLibraries.flatMap { $0.identities.map { $0.ref } }) - } - + public func solve(constraints: [Constraint]) -> Result<[DependencyResolverBinding], Error> { // the graph resolution root let root: DependencyResolutionNode if constraints.count == 1, let constraint = constraints.first, constraint.package.kind.isRoot { @@ -159,26 +161,13 @@ public struct PubGrubDependencyResolver { } do { - // Use empty `availableLibraries` for the rest of resolving if we don't prefer them. - let availableLibraries = preferPrebuiltLibraries ? availableLibraries : [] - // strips state - let bindings = try self.solve(root: root, constraints: constraints, availableLibraries: availableLibraries).bindings.filter { - return $0.package.matchingPrebuiltLibrary(in: availableLibraries) == nil - } + let bindings = try self.solve(root: root, constraints: constraints).bindings return .success(bindings) } catch { // If version solving failing, build the user-facing diagnostic. - if let pubGrubError = error as? PubgrubError, let rootCause = pubGrubError.rootCause, let incompatibilities = pubGrubError.incompatibilities { - let incompatiblePackages = incompatibilities.map({ $0.key.package }) - if incompatiblePackages.contains(where: { $0.matchingPrebuiltLibrary(in: availableLibraries) != nil }) { - return .failure( - PubgrubError.potentiallyUnresovableDueToPrebuiltLibrary( - incompatiblePackages, - pubGrubError.description - ) - ) - } - + if let pubGrubError = error as? PubgrubError, let rootCause = pubGrubError.rootCause, + let incompatibilities = pubGrubError.incompatibilities + { do { var builder = DiagnosticReportBuilder( root: root, @@ -199,12 +188,12 @@ public struct PubGrubDependencyResolver { /// Find a set of dependencies that fit the given constraints. If dependency /// resolution is unable to provide a result, an error is thrown. /// - Warning: It is expected that the root package reference has been set before this is called. - internal func solve(root: DependencyResolutionNode, constraints: [Constraint], availableLibraries: [LibraryMetadata] = []) throws -> ( + func solve(root: DependencyResolutionNode, constraints: [Constraint]) throws -> ( bindings: [DependencyResolverBinding], state: State ) { // first process inputs - let inputs = try self.processInputs(root: root, with: constraints, availableLibraries: availableLibraries) + let inputs = try self.processInputs(root: root, with: constraints) // Prefetch the containers if prefetching is enabled. if self.prefetchBasedOnResolvedFile { @@ -214,7 +203,7 @@ public struct PubGrubDependencyResolver { let pins = self.pins.values .map(\.packageRef) .filter { !inputs.overriddenPackages.keys.contains($0) } - self.provider.prefetch(containers: pins, availableLibraries: availableLibraries) + self.provider.prefetch(containers: pins, availableLibraries: self.availableLibraries) } let state = State(root: root, overriddenPackages: inputs.overriddenPackages) @@ -223,14 +212,17 @@ public struct PubGrubDependencyResolver { state.decide(state.root, at: "1.0.0") // Add the root incompatibility. - state.addIncompatibility(Incompatibility(terms: [Term(not: root, .exact("1.0.0"))], cause: .root), at: .topLevel) + state.addIncompatibility( + Incompatibility(terms: [Term(not: root, .exact("1.0.0"))], cause: .root), + at: .topLevel + ) // Add inputs root incompatibilities. for incompatibility in inputs.rootIncompatibilities { state.addIncompatibility(incompatibility, at: .topLevel) } - try self.run(state: state, availableLibraries: availableLibraries) + try self.run(state: state) let decisions = state.solution.assignments.filter(\.isDecision) var flattenedAssignments: [PackageReference: (binding: BoundVersion, products: ProductFilter)] = [:] @@ -239,6 +231,8 @@ public struct PubGrubDependencyResolver { continue } + let package = assignment.term.node.package + let boundVersion: BoundVersion switch assignment.term.requirement { case .exact(let version): @@ -247,15 +241,29 @@ public struct PubGrubDependencyResolver { throw InternalError("unexpected requirement value for assignment \(assignment.term)") } + // Strip packages that have prebuilt libraries only if they match library version. + // + // FIXME: This is built on assumption that libraries are part of the SDK and are + // always available in include/library paths, but what happens if they are + // part of a toolchain instead? Builder needs an indicator that certain path + // has to be included when building packages that depend on prebuilt libraries. + if let library = package.matchingPrebuiltLibrary(in: availableLibraries), + boundVersion == .version(.init(stringLiteral: library.version)) + { + continue + } + let products = assignment.term.node.productFilter // TODO: replace with async/await when available - let container = try temp_await { provider.getContainer(for: assignment.term.node.package, availableLibraries: availableLibraries, completion: $0) } + let container = try temp_await { self.provider.getContainer(for: package, completion: $0) } let updatePackage = try container.underlying.loadPackageReference(at: boundVersion) if var existing = flattenedAssignments[updatePackage] { guard existing.binding == boundVersion else { - throw InternalError("Two products in one package resolved to different versions: \(existing.products)@\(existing.binding) vs \(products)@\(boundVersion)") + throw InternalError( + "Two products in one package resolved to different versions: \(existing.products)@\(existing.binding) vs \(products)@\(boundVersion)" + ) } existing.products.formUnion(products) flattenedAssignments[updatePackage] = existing @@ -272,12 +280,12 @@ public struct PubGrubDependencyResolver { // Add overridden packages to the result. for (package, override) in state.overriddenPackages { // TODO: replace with async/await when available - let container = try temp_await { provider.getContainer(for: package, availableLibraries: availableLibraries, completion: $0) } + let container = try temp_await { self.provider.getContainer(for: package, completion: $0) } let updatePackage = try container.underlying.loadPackageReference(at: override.version) finalAssignments.append(.init( - package: updatePackage, - boundVersion: override.version, - products: override.products + package: updatePackage, + boundVersion: override.version, + products: override.products )) } @@ -288,8 +296,7 @@ public struct PubGrubDependencyResolver { private func processInputs( root: DependencyResolutionNode, - with constraints: [Constraint], - availableLibraries: [LibraryMetadata] + with constraints: [Constraint] ) throws -> ( overriddenPackages: [PackageReference: (version: BoundVersion, products: ProductFilter)], rootIncompatibilities: [Incompatibility] @@ -307,7 +314,10 @@ public struct PubGrubDependencyResolver { // The list of version-based references reachable via local and branch-based references. // These are added as top-level incompatibilities since they always need to be satisfied. // Some of these might be overridden as we discover local and branch-based references. - var versionBasedDependencies = OrderedCollections.OrderedDictionary() + var versionBasedDependencies = OrderedCollections.OrderedDictionary< + DependencyResolutionNode, + [VersionBasedConstraint] + >() // Process unversioned constraints in first phase. We go through all of the unversioned packages // and collect them and their dependencies. This gives us the complete list of unversioned @@ -319,7 +329,9 @@ public struct PubGrubDependencyResolver { // Mark the package as overridden. if var existing = overriddenPackages[constraint.package] { guard existing.version == .unversioned else { - throw InternalError("Overridden package is not unversioned: \(constraint.package)@\(existing.version)") + throw InternalError( + "Overridden package is not unversioned: \(constraint.package)@\(existing.version)" + ) } existing.products.formUnion(constraint.products) overriddenPackages[constraint.package] = existing @@ -331,11 +343,13 @@ public struct PubGrubDependencyResolver { // Process dependencies of this package. // // We collect all version-based dependencies in a separate structure so they can - // be process at the end. This allows us to override them when there is a non-version + // be processed at the end. This allows us to override them when there is a non-version // based (unversioned/branch-based) constraint present in the graph. // TODO: replace with async/await when available - let container = try temp_await { provider.getContainer(for: node.package, availableLibraries: availableLibraries, completion: $0) } - for dependency in try container.underlying.getUnversionedDependencies(productFilter: node.productFilter) { + let container = try temp_await { self.provider.getContainer(for: node.package, completion: $0) } + for dependency in try container.underlying + .getUnversionedDependencies(productFilter: node.productFilter) + { if let versionedBasedConstraints = VersionBasedConstraint.constraints(dependency) { for constraint in versionedBasedConstraints { versionBasedDependencies[node, default: []].append(constraint) @@ -370,9 +384,13 @@ public struct PubGrubDependencyResolver { case .revision(let existingRevision, let branch)?: // If this branch-based package was encountered before, ensure the references match. if (branch ?? existingRevision) != revision { - throw PubgrubError.unresolvable("\(package.identity) is required using two different revision-based requirements (\(existingRevision) and \(revision)), which is not supported") + throw PubgrubError + .unresolvable( + "\(package.identity) is required using two different revision-based requirements (\(existingRevision) and \(revision)), which is not supported" + ) } else { - // Otherwise, continue since we've already processed this constraint. Any cycles will be diagnosed separately. + // Otherwise, continue since we've already processed this constraint. Any cycles will be diagnosed + // separately. continue } case nil: @@ -382,7 +400,7 @@ public struct PubGrubDependencyResolver { // Process dependencies of this package, similar to the first phase but branch-based dependencies // are not allowed to contain local/unversioned packages. // TODO: replace with async/await when avail - let container = try temp_await { provider.getContainer(for: package, availableLibraries: availableLibraries, completion: $0) } + let container = try temp_await { self.provider.getContainer(for: package, completion: $0) } // If there is a pin for this revision-based dependency, get // the dependencies at the pinned revision instead of using @@ -393,7 +411,10 @@ public struct PubGrubDependencyResolver { revisionForDependencies = pinRevision // Mark the package as overridden with the pinned revision and record the branch as well. - overriddenPackages[package] = (version: .revision(revisionForDependencies, branch: revision), products: constraint.products) + overriddenPackages[package] = ( + version: .revision(revisionForDependencies, branch: revision), + products: constraint.products + ) } else { revisionForDependencies = revision @@ -414,7 +435,8 @@ public struct PubGrubDependencyResolver { case .versionSet(let req): for node in dependency.nodes() { let versionedBasedConstraint = VersionBasedConstraint(node: node, req: req) - versionBasedDependencies[.root(package: constraint.package), default: []].append(versionedBasedConstraint) + versionBasedDependencies[.root(package: constraint.package), default: []] + .append(versionedBasedConstraint) } case .revision: constraints.append(dependency) @@ -438,12 +460,15 @@ public struct PubGrubDependencyResolver { versionBasedDependencies[root, default: []].append(versionedBasedConstraint) } case .revision, .unversioned: - throw InternalError("Unexpected revision/unversioned requirement in the constraints list: \(constraints)") + throw InternalError( + "Unexpected revision/unversioned requirement in the constraints list: \(constraints)" + ) } } // Finally, compute the root incompatibilities (which will be all version-based). - // note versionBasedDependencies may point to the root package dependencies, or the dependencies of root's non-versioned dependencies + // note versionBasedDependencies may point to the root package dependencies, or the dependencies of root's + // non-versioned dependencies var rootIncompatibilities: [Incompatibility] = [] for (node, constraints) in versionBasedDependencies { for constraint in constraints { @@ -465,7 +490,7 @@ public struct PubGrubDependencyResolver { /// decisions if nothing else is left to be done. /// After this method returns `solution` is either populated with a list of /// final version assignments or an error is thrown. - private func run(state: State, availableLibraries: [LibraryMetadata]) throws { + private func run(state: State) throws { var next: DependencyResolutionNode? = state.root while let nxt = next { @@ -474,13 +499,13 @@ public struct PubGrubDependencyResolver { // initiate prefetch of known packages that will be used to make the decision on the next step self.provider.prefetch( containers: state.solution.undecided.map(\.node.package), - availableLibraries: availableLibraries + availableLibraries: self.availableLibraries ) // If decision making determines that no more decisions are to be // made, it returns nil to signal that version solving is done. // TODO: replace with async/await when available - next = try temp_await { self.makeDecision(state: state, availableLibraries: availableLibraries, completion: $0) } + next = try temp_await { self.makeDecision(state: state, completion: $0) } } } @@ -488,7 +513,7 @@ public struct PubGrubDependencyResolver { /// partial solution. /// If a conflict is found, the conflicting incompatibility is returned to /// resolve the conflict on. - internal func propagate(state: State, node: DependencyResolutionNode) throws { + func propagate(state: State, node: DependencyResolutionNode) throws { var changed: OrderedCollections.OrderedSet = [node] while !changed.isEmpty { @@ -543,7 +568,6 @@ public struct PubGrubDependencyResolver { return .conflict } - state.derive(unsatisfiedTerm.inverse, cause: incompatibility) self.delegate?.derived(term: unsatisfiedTerm.inverse) return .almostSatisfied(node: unsatisfiedTerm.node) @@ -552,7 +576,7 @@ public struct PubGrubDependencyResolver { // Based on: // https://github.com/dart-lang/pub/tree/master/doc/solver.md#conflict-resolution // https://github.com/dart-lang/pub/blob/master/lib/src/solver/version_solver.dart#L201 - internal func resolve(state: State, conflict: Incompatibility) throws -> Incompatibility { + func resolve(state: State, conflict: Incompatibility) throws -> Incompatibility { self.delegate?.conflict(conflict: conflict) var incompatibility = conflict @@ -563,7 +587,7 @@ public struct PubGrubDependencyResolver { let maxIterations = 1000 var iterations = 0 - while !isCompleteFailure(incompatibility, root: state.root) { + while !self.isCompleteFailure(incompatibility, root: state.root) { var mostRecentTerm: Term? var mostRecentSatisfier: Assignment? var difference: Term? @@ -592,7 +616,10 @@ public struct PubGrubDependencyResolver { if mostRecentTerm == term { difference = mostRecentSatisfier?.term.difference(with: term) if let difference { - previousSatisfierLevel = max(previousSatisfierLevel, try state.solution.satisfier(for: difference.inverse).decisionLevel) + previousSatisfierLevel = try max( + previousSatisfierLevel, + state.solution.satisfier(for: difference.inverse).decisionLevel + ) } } } @@ -631,9 +658,18 @@ public struct PubGrubDependencyResolver { if let mostRecentTerm { if let difference { - self.delegate?.partiallySatisfied(term: mostRecentTerm, by: _mostRecentSatisfier, incompatibility: incompatibility, difference: difference) + self.delegate?.partiallySatisfied( + term: mostRecentTerm, + by: _mostRecentSatisfier, + incompatibility: incompatibility, + difference: difference + ) } else { - self.delegate?.satisfied(term: mostRecentTerm, by: _mostRecentSatisfier, incompatibility: incompatibility) + self.delegate?.satisfied( + term: mostRecentTerm, + by: _mostRecentSatisfier, + incompatibility: incompatibility + ) } } @@ -658,7 +694,6 @@ public struct PubGrubDependencyResolver { private func computeCounts( for terms: [Term], - availableLibraries: [LibraryMetadata], completion: @escaping (Result<[Term: Int], Error>) -> Void ) { if terms.isEmpty { @@ -668,26 +703,26 @@ public struct PubGrubDependencyResolver { let sync = DispatchGroup() let results = ThreadSafeKeyValueStore>() - terms.forEach { term in + for term in terms { sync.enter() - provider.getContainer(for: term.node.package, availableLibraries: availableLibraries) { result in + self.provider.getContainer(for: term.node.package) { result in defer { sync.leave() } - results[term] = result.flatMap { container in Result(catching: { try container.versionCount(term.requirement) }) } + results[term] = result + .flatMap { container in Result(catching: { try container.versionCount(term.requirement) }) } } } sync.notify(queue: .sharedConcurrent) { do { - completion(.success(try results.mapValues { try $0.get() })) + try completion(.success(results.mapValues { try $0.get() })) } catch { completion(.failure(error)) } } } - internal func makeDecision( + func makeDecision( state: State, - availableLibraries: [LibraryMetadata] = [], completion: @escaping (Result) -> Void ) { // If there are no more undecided terms, version solving is complete. @@ -696,9 +731,33 @@ public struct PubGrubDependencyResolver { return completion(.success(nil)) } + // If prebuilt libraries are available, let's attempt their versions first before going for + // the latest viable version in the package. This way we archive multiple goals - prioritize + // prebuilt libraries if they satisfy all requirements, avoid counting and building package + // manifests and avoid (re-)building packages. + // + // Since the conflict resolution learns from incorrect terms this wouldn't be re-attempted. + if !self.availableLibraries.isEmpty { + let start = DispatchTime.now() + for pkgTerm in undecided { + let package = pkgTerm.node.package + guard let library = package.matchingPrebuiltLibrary(in: self.availableLibraries) else { + continue + } + + let version = Version(stringLiteral: library.version) + + if pkgTerm.requirement.contains(version) { + self.delegate?.didResolve(term: pkgTerm, version: version, duration: start.distance(to: .now())) + state.decide(pkgTerm.node, at: version) + return completion(.success(pkgTerm.node)) + } + } + } + // Prefer packages with least number of versions that fit the current requirements so we // get conflicts (if any) sooner. - self.computeCounts(for: undecided, availableLibraries: availableLibraries) { result in + self.computeCounts(for: undecided) { result in do { let start = DispatchTime.now() let counts = try result.get() @@ -710,7 +769,10 @@ public struct PubGrubDependencyResolver { // Get the best available version for this package. guard let version = try container.getBestAvailableVersion(for: pkgTerm) else { - state.addIncompatibility(try Incompatibility(pkgTerm, root: state.root, cause: .noAvailableVersion), at: .decisionMaking) + try state.addIncompatibility( + Incompatibility(pkgTerm, root: state.root, cause: .noAvailableVersion), + at: .decisionMaking + ) return completion(.success(pkgTerm.node)) } @@ -751,18 +813,17 @@ public struct PubGrubDependencyResolver { } } -internal enum LogLocation: String { +enum LogLocation: String { case topLevel = "top level" case unitPropagation = "unit propagation" case decisionMaking = "decision making" case conflictResolution = "conflict resolution" } -public extension PubGrubDependencyResolver { - enum PubgrubError: Swift.Error, CustomStringConvertible { +extension PubGrubDependencyResolver { + public enum PubgrubError: Swift.Error, CustomStringConvertible { case _unresolvable(Incompatibility, [DependencyResolutionNode: [Incompatibility]]) case unresolvable(String) - case potentiallyUnresovableDueToPrebuiltLibrary([PackageReference], String) public var description: String { switch self { @@ -770,8 +831,6 @@ public extension PubGrubDependencyResolver { return rootCause.description case .unresolvable(let error): return error - case .potentiallyUnresovableDueToPrebuiltLibrary(_, let error): - return error } } @@ -781,8 +840,6 @@ public extension PubGrubDependencyResolver { return rootCause case .unresolvable: return nil - case .potentiallyUnresovableDueToPrebuiltLibrary: - return nil } } @@ -792,8 +849,6 @@ public extension PubGrubDependencyResolver { return incompatibilities case .unresolvable: return nil - case .potentiallyUnresovableDueToPrebuiltLibrary: - return nil } } } @@ -809,7 +864,7 @@ extension PubGrubDependencyResolver { self.requirement = req } - internal static func constraints(_ constraint: Constraint) -> [VersionBasedConstraint]? { + static func constraints(_ constraint: Constraint) -> [VersionBasedConstraint]? { switch constraint.requirement { case .versionSet(let req): return constraint.nodes().map { VersionBasedConstraint(node: $0, req: req) } @@ -828,8 +883,8 @@ private enum PropagationResult { case none } -private extension PackageRequirement { - var isRevision: Bool { +extension PackageRequirement { + fileprivate var isRevision: Bool { switch self { case .versionSet, .unversioned: return false @@ -852,8 +907,10 @@ extension PackageReference { scope: registryIdentity.scope.description, name: registryIdentity.name.description ) - }) - }) + } + ) + } + ) } else { return nil } diff --git a/Sources/SPMTestSupport/Resolver.swift b/Sources/SPMTestSupport/Resolver.swift deleted file mode 100644 index 1e1282b7552..00000000000 --- a/Sources/SPMTestSupport/Resolver.swift +++ /dev/null @@ -1,19 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift open source project -// -// Copyright (c) 2024 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See http://swift.org/LICENSE.txt for license information -// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import PackageGraph - -extension PubGrubDependencyResolver { - package func solve(constraints: [Constraint]) -> Result<[DependencyResolverBinding], Error> { - return solve(constraints: constraints, availableLibraries: [], preferPrebuiltLibraries: false) - } -} diff --git a/Sources/Workspace/ResolverPrecomputationProvider.swift b/Sources/Workspace/ResolverPrecomputationProvider.swift index 3d9803adea7..7308bbcfcac 100644 --- a/Sources/Workspace/ResolverPrecomputationProvider.swift +++ b/Sources/Workspace/ResolverPrecomputationProvider.swift @@ -44,19 +44,14 @@ struct ResolverPrecomputationProvider: PackageContainerProvider { /// The tools version currently in use. let currentToolsVersion: ToolsVersion - /// The available libraries in the SDK. - let availableLibraries: [LibraryMetadata] - init( root: PackageGraphRoot, dependencyManifests: Workspace.DependencyManifests, - currentToolsVersion: ToolsVersion = ToolsVersion.current, - availableLibraries: [LibraryMetadata] + currentToolsVersion: ToolsVersion = ToolsVersion.current ) { self.root = root self.dependencyManifests = dependencyManifests self.currentToolsVersion = currentToolsVersion - self.availableLibraries = availableLibraries } func getContainer( @@ -68,7 +63,9 @@ struct ResolverPrecomputationProvider: PackageContainerProvider { ) { queue.async { // Start by searching manifests from the Workspace's resolved dependencies. - if let manifest = self.dependencyManifests.dependencies.first(where: { _, managed, _, _ in managed.packageRef == package }) { + if let manifest = self.dependencyManifests.dependencies + .first(where: { _, managed, _, _ in managed.packageRef == package }) + { let container = LocalPackageContainer( package: package, manifest: manifest.manifest, @@ -89,15 +86,6 @@ struct ResolverPrecomputationProvider: PackageContainerProvider { return completion(.success(container)) } - // Match against available prebuilt libraries. - if let matchingPrebuiltLibrary = package.matchingPrebuiltLibrary(in: availableLibraries) { - do { - return completion(.success(try PrebuiltPackageContainer(metadata: matchingPrebuiltLibrary))) - } catch { - return completion(.failure(error)) - } - } - // As we don't have anything else locally, error out. completion(.failure(ResolverPrecomputationError.missingPackage(package: package))) } @@ -113,7 +101,7 @@ private struct LocalPackageContainer: PackageContainer { let shouldInvalidatePinnedVersions = false func versionsAscending() throws -> [Version] { - switch dependency?.state { + switch self.dependency?.state { case .sourceControlCheckout(.version(let version, revision: _)): return [version] case .registryDownload(let version): @@ -125,7 +113,10 @@ private struct LocalPackageContainer: PackageContainer { func isToolsVersionCompatible(at version: Version) -> Bool { do { - try manifest.toolsVersion.validateToolsVersion(currentToolsVersion, packageIdentity: .plain("unknown")) + try self.manifest.toolsVersion.validateToolsVersion( + self.currentToolsVersion, + packageIdentity: .plain("unknown") + ) return true } catch { return false @@ -133,31 +124,36 @@ private struct LocalPackageContainer: PackageContainer { } func toolsVersion(for version: Version) throws -> ToolsVersion { - return currentToolsVersion + self.currentToolsVersion } func toolsVersionsAppropriateVersionsDescending() throws -> [Version] { - return try self.versionsDescending() + try self.versionsDescending() } func getDependencies(at version: Version, productFilter: ProductFilter) throws -> [PackageContainerConstraint] { // Because of the implementation of `reversedVersions`, we should only get the exact same version. - switch dependency?.state { + switch self.dependency?.state { case .sourceControlCheckout(.version(version, revision: _)): - return try manifest.dependencyConstraints(productFilter: productFilter) + return try self.manifest.dependencyConstraints(productFilter: productFilter) case .registryDownload(version: version): - return try manifest.dependencyConstraints(productFilter: productFilter) + return try self.manifest.dependencyConstraints(productFilter: productFilter) default: - throw InternalError("expected version based state, but state was \(String(describing: dependency?.state))") + throw InternalError( + "expected version based state, but state was \(String(describing: self.dependency?.state))" + ) } } - func getDependencies(at revisionString: String, productFilter: ProductFilter) throws -> [PackageContainerConstraint] { + func getDependencies( + at revisionString: String, + productFilter: ProductFilter + ) throws -> [PackageContainerConstraint] { let revision = Revision(identifier: revisionString) - switch dependency?.state { + switch self.dependency?.state { case .sourceControlCheckout(.branch(_, revision: revision)), .sourceControlCheckout(.revision(revision)): // Return the dependencies if the checkout state matches the revision. - return try manifest.dependencyConstraints(productFilter: productFilter) + return try self.manifest.dependencyConstraints(productFilter: productFilter) default: // Throw an error when the dependency is not revision based to fail resolution. throw ResolverPrecomputationError.differentRequirement( @@ -169,14 +165,14 @@ private struct LocalPackageContainer: PackageContainer { } func getUnversionedDependencies(productFilter: ProductFilter) throws -> [PackageContainerConstraint] { - switch dependency?.state { + switch self.dependency?.state { case .none, .fileSystem, .edited: - return try manifest.dependencyConstraints(productFilter: productFilter) + return try self.manifest.dependencyConstraints(productFilter: productFilter) default: // Throw an error when the dependency is not unversioned to fail resolution. throw ResolverPrecomputationError.differentRequirement( - package: package, - state: dependency?.state, + package: self.package, + state: self.dependency?.state, requirement: .unversioned ) } diff --git a/Sources/Workspace/Workspace+Dependencies.swift b/Sources/Workspace/Workspace+Dependencies.swift index 3157260e268..5ece297dc4a 100644 --- a/Sources/Workspace/Workspace+Dependencies.swift +++ b/Sources/Workspace/Workspace+Dependencies.swift @@ -121,12 +121,15 @@ extension Workspace { } // Resolve the dependencies. - let resolver = try self.createResolver(pins: pins, observabilityScope: observabilityScope) + let resolver = try self.createResolver( + pins: pins, + availableLibraries: availableLibraries, + observabilityScope: observabilityScope + ) self.activeResolver = resolver let updateResults = self.resolveDependencies( resolver: resolver, - availableLibraries: availableLibraries, constraints: updateConstraints, observabilityScope: observabilityScope ) @@ -591,12 +594,15 @@ extension Workspace { computedConstraints += try graphRoot.constraints() + constraints // Perform dependency resolution. - let resolver = try self.createResolver(pins: pinsStore.pins, observabilityScope: observabilityScope) + let resolver = try self.createResolver( + pins: pinsStore.pins, + availableLibraries: availableLibraries, + observabilityScope: observabilityScope + ) self.activeResolver = resolver let result = self.resolveDependencies( resolver: resolver, - availableLibraries: availableLibraries, constraints: computedConstraints, observabilityScope: observabilityScope ) @@ -842,19 +848,15 @@ extension Workspace { let precomputationProvider = ResolverPrecomputationProvider( root: root, - dependencyManifests: dependencyManifests, - availableLibraries: availableLibraries + dependencyManifests: dependencyManifests ) let resolver = PubGrubDependencyResolver( provider: precomputationProvider, pins: pinsStore.pins, - observabilityScope: observabilityScope - ) - let result = resolver.solve( - constraints: computedConstraints, availableLibraries: availableLibraries, - preferPrebuiltLibraries: true + observabilityScope: observabilityScope ) + let result = resolver.solve(constraints: computedConstraints) guard !observabilityScope.errorsReported else { return .required(reason: .errorsPreviouslyReported) @@ -1051,7 +1053,9 @@ extension Workspace { completion: $0 ) }) as? SourceControlPackageContainer else { - throw InternalError("invalid container for \(binding.package) expected a SourceControlPackageContainer") + throw InternalError( + "invalid container for \(binding.package) expected a SourceControlPackageContainer" + ) } var revision = try container.getRevision(forIdentifier: identifier) let branch = branch ?? (identifier == revision.identifier ? nil : identifier) @@ -1122,6 +1126,7 @@ extension Workspace { /// Creates resolver for the workspace. fileprivate func createResolver( pins: PinsStore.Pins, + availableLibraries: [LibraryMetadata], observabilityScope: ObservabilityScope ) throws -> PubGrubDependencyResolver { var delegate: DependencyResolverDelegate @@ -1138,6 +1143,7 @@ extension Workspace { return PubGrubDependencyResolver( provider: packageContainerProvider, pins: pins, + availableLibraries: availableLibraries, skipDependenciesUpdates: self.configuration.skipDependenciesUpdates, prefetchBasedOnResolvedFile: self.configuration.prefetchBasedOnResolvedFile, observabilityScope: observabilityScope, @@ -1148,24 +1154,12 @@ extension Workspace { /// Runs the dependency resolver based on constraints provided and returns the results. fileprivate func resolveDependencies( resolver: PubGrubDependencyResolver, - availableLibraries: [LibraryMetadata], constraints: [PackageContainerConstraint], observabilityScope: ObservabilityScope ) -> [DependencyResolverBinding] { os_signpost(.begin, name: SignpostName.pubgrub) - var result = resolver.solve( - constraints: constraints, - availableLibraries: availableLibraries, - preferPrebuiltLibraries: true - ) - // If the initial resolution failed due to prebuilt libraries, we try to resolve again without prebuilt libraries. - if case let Result.failure(error as PubGrubDependencyResolver.PubgrubError) = result, case .potentiallyUnresovableDueToPrebuiltLibrary = error { - result = resolver.solve( - constraints: constraints, - availableLibraries: availableLibraries, - preferPrebuiltLibraries: false - ) - } + let result = resolver.solve(constraints: constraints) + os_signpost(.end, name: SignpostName.pubgrub) // Take an action based on the result. diff --git a/Tests/PackageGraphTests/PubgrubTests.swift b/Tests/PackageGraphTests/PubgrubTests.swift index b05bd760340..7684413fa48 100644 --- a/Tests/PackageGraphTests/PubgrubTests.swift +++ b/Tests/PackageGraphTests/PubgrubTests.swift @@ -54,11 +54,11 @@ private let v1_1: Version = "1.1.0" private let v1_5: Version = "1.5.0" private let v2: Version = "2.0.0" private let v3: Version = "3.0.0" -private let v1Range: VersionSetSpecifier = .range(v1..=1.0.0 <1.5.0 XCTAssertEqual( Term("a^1.0.0").intersect(with: Term("¬a@1.5.0")), - Term("a-1.0.0-1.5.0")) + Term("a-1.0.0-1.5.0") + ) // a^1.0.0 ∩ a >=1.5.0 <3.0.0 → a^1.5.0 XCTAssertEqual( Term("a^1.0.0").intersect(with: Term("a-1.5.0-3.0.0")), - Term("a^1.5.0")) + Term("a^1.5.0") + ) // ¬a^1.0.0 ∩ ¬a >=1.5.0 <3.0.0 → ¬a >=1.0.0 <3.0.0 XCTAssertEqual( Term("¬a^1.0.0").intersect(with: Term("¬a-1.5.0-3.0.0")), - Term("¬a-1.0.0-3.0.0")) + Term("¬a-1.0.0-3.0.0") + ) XCTAssertEqual( Term("a^1.0.0").intersect(with: Term("a^1.0.0")), - Term("a^1.0.0")) + Term("a^1.0.0") + ) XCTAssertEqual( Term("¬a^1.0.0").intersect(with: Term("¬a^1.0.0")), - Term("¬a^1.0.0")) + Term("¬a^1.0.0") + ) XCTAssertNil(Term("a^1.0.0").intersect(with: Term("¬a^1.0.0"))) XCTAssertNil(Term("a@1.0.0").difference(with: Term("a@1.0.0"))) XCTAssertEqual( Term("¬a^1.0.0").intersect(with: Term("a^2.0.0")), - Term("a^2.0.0")) + Term("a^2.0.0") + ) XCTAssertEqual( Term("a^2.0.0").intersect(with: Term("¬a^1.0.0")), - Term("a^2.0.0")) + Term("a^2.0.0") + ) XCTAssertEqual( Term("¬a^1.0.0").intersect(with: Term("¬a^1.0.0")), - Term("¬a^1.0.0")) + Term("¬a^1.0.0") + ) XCTAssertEqual( Term("¬a@1.0.0").intersect(with: Term("¬a@1.0.0")), - Term("¬a@1.0.0")) + Term("¬a@1.0.0") + ) // Check difference. let anyA = Term(.empty(package: "a"), .any) @@ -184,7 +193,7 @@ final class PubgrubTests: XCTestCase { func testTermIsValidDecision() { let solution100_150 = PartialSolution(assignments: [ .derivation("a^1.0.0", cause: _cause, decisionLevel: 1), - .derivation("a^1.5.0", cause: _cause, decisionLevel: 2) + .derivation("a^1.5.0", cause: _cause, decisionLevel: 2), ]) let allSatisfied = Term("a@1.6.0") @@ -194,26 +203,33 @@ final class PubgrubTests: XCTestCase { } func testIncompatibilityNormalizeTermsOnInit() throws { - let i1 = try Incompatibility(Term("a^1.0.0"), Term("a^1.5.0"), Term("¬b@1.0.0"), - root: rootNode) + let i1 = try Incompatibility( + Term("a^1.0.0"), + Term("a^1.5.0"), + Term("¬b@1.0.0"), + root: rootNode + ) XCTAssertEqual(i1.terms.count, 2) let a1 = i1.terms.first { $0.node.package == "a" } let b1 = i1.terms.first { $0.node.package == "b" } XCTAssertEqual(a1?.requirement, v1_5Range) XCTAssertEqual(b1?.requirement, .exact(v1)) - let i2 = try Incompatibility(Term("¬a^1.0.0"), Term("a^2.0.0"), - root: rootNode) + let i2 = try Incompatibility( + Term("¬a^1.0.0"), + Term("a^2.0.0"), + root: rootNode + ) XCTAssertEqual(i2.terms.count, 1) let a2 = i2.terms.first XCTAssertEqual(a2?.requirement, v2Range) } func testSolutionPositive() { - let s1 = PartialSolution(assignments:[ + let s1 = PartialSolution(assignments: [ .derivation("a^1.5.0", cause: _cause, decisionLevel: 0), .derivation("b@2.0.0", cause: _cause, decisionLevel: 0), - .derivation("a^1.0.0", cause: _cause, decisionLevel: 0) + .derivation("a^1.0.0", cause: _cause, decisionLevel: 0), ]) let a1 = s1._positive.first { $0.key.package.identity == PackageIdentity("a") }?.value XCTAssertEqual(a1?.requirement, v1_5Range) @@ -222,10 +238,10 @@ final class PubgrubTests: XCTestCase { let s2 = PartialSolution(assignments: [ .derivation("¬a^1.5.0", cause: _cause, decisionLevel: 0), - .derivation("a^1.0.0", cause: _cause, decisionLevel: 0) + .derivation("a^1.0.0", cause: _cause, decisionLevel: 0), ]) let a2 = s2._positive.first { $0.key.package.identity == PackageIdentity("a") }?.value - XCTAssertEqual(a2?.requirement, .range(v1.. non-versioned -> version func testHappyPath2() throws { try builder.serve("foo", at: v1_1, with: ["foo": ["config": (.versionSet(v1Range), .specific(["config"]))]]) - try builder.serve("bar", at: .unversioned, with: ["bar": ["config": (.versionSet(v1_1Range), .specific(["config"]))]]) - try builder.serve("config", at: v1) - try builder.serve("config", at: v1_1) + try builder.serve( + "bar", + at: .unversioned, + with: ["bar": ["config": (.versionSet(v1_1Range), .specific(["config"]))]] + ) + try builder.serve("config", at: v1) + try builder.serve("config", at: v1_1) let resolver = builder.create() let dependencies = try builder.create(dependencies: [ "foo": (.versionSet(v1Range), .specific(["foo"])), - "bar": (.unversioned, .specific(["bar"])) + "bar": (.unversioned, .specific(["bar"])), ]) let result = resolver.solve(constraints: dependencies) @@ -997,14 +1091,18 @@ final class PubgrubTests: XCTestCase { func testHappyPath3() throws { try builder.serve("foo", at: v1_1, with: ["foo": ["config": (.versionSet(v1Range), .specific(["config"]))]]) try builder.serve("bar", at: .unversioned, with: ["bar": ["baz": (.unversioned, .specific(["baz"]))]]) - try builder.serve("baz", at: .unversioned, with: ["baz": ["config": (.versionSet(v1_1Range), .specific(["config"]))]]) + try builder.serve( + "baz", + at: .unversioned, + with: ["baz": ["config": (.versionSet(v1_1Range), .specific(["config"]))]] + ) try builder.serve("config", at: v1) try builder.serve("config", at: v1_1) let resolver = builder.create() let dependencies = try builder.create(dependencies: [ "foo": (.versionSet(v1Range), .specific(["foo"])), - "bar": (.unversioned, .specific(["bar"])) + "bar": (.unversioned, .specific(["bar"])), ]) let result = resolver.solve(constraints: dependencies) @@ -1027,7 +1125,7 @@ final class PubgrubTests: XCTestCase { let resolver = builder.create() let dependencies = try builder.create(dependencies: [ "config": (.versionSet(v1Range), .specific(["config"])), - "foo": (.versionSet(v1Range), .specific(["foo"])) + "foo": (.versionSet(v1Range), .specific(["foo"])), ]) let result = resolver.solve(constraints: dependencies) @@ -1041,14 +1139,18 @@ final class PubgrubTests: XCTestCase { // root -> version // root -> non-versioned -> version func testHappyPath5() throws { - try builder.serve("foo", at: .unversioned, with: ["foo": ["config": (.versionSet(v1_1Range), .specific(["config"]))]]) + try builder.serve( + "foo", + at: .unversioned, + with: ["foo": ["config": (.versionSet(v1_1Range), .specific(["config"]))]] + ) try builder.serve("config", at: v1) try builder.serve("config", at: v1_1) let resolver = builder.create() let dependencies = try builder.create(dependencies: [ "config": (.versionSet(v1Range), .specific(["config"])), - "foo": (.unversioned, .specific(["foo"])) + "foo": (.unversioned, .specific(["foo"])), ]) let result = resolver.solve(constraints: dependencies) @@ -1059,19 +1161,22 @@ final class PubgrubTests: XCTestCase { ]) } - // root -> version // root -> non-versioned -> non-versioned -> version func testHappyPath6() throws { try builder.serve("foo", at: .unversioned, with: ["foo": ["bar": (.unversioned, .specific(["bar"]))]]) - try builder.serve("bar", at: .unversioned, with: ["bar": ["config": (.versionSet(v1_1Range), .specific(["config"]))]]) + try builder.serve( + "bar", + at: .unversioned, + with: ["bar": ["config": (.versionSet(v1_1Range), .specific(["config"]))]] + ) try builder.serve("config", at: v1) try builder.serve("config", at: v1_1) let resolver = builder.create() let dependencies = try builder.create(dependencies: [ "config": (.versionSet(v1Range), .specific(["config"])), - "foo": (.unversioned, .specific(["foo"])) + "foo": (.unversioned, .specific(["foo"])), ]) let result = resolver.solve(constraints: dependencies) @@ -1090,15 +1195,16 @@ final class PubgrubTests: XCTestCase { try builder.serve(package, at: .unversioned, with: [ "module": [ "config": (.versionSet(v1Range), .specific(["config"])), - "foo": (.versionSet(v1Range), .specific(["foo"])) - ]]) + "foo": (.versionSet(v1Range), .specific(["foo"])), + ], + ]) try builder.serve("foo", at: v1_1, with: ["foo": ["config": (.versionSet(v1_1Range), .specific(["config"]))]]) try builder.serve("config", at: v1) try builder.serve("config", at: v1_1) let resolver = builder.create() let dependencies = builder.create(dependencies: [ - package: (.unversioned, .everything) + package: (.unversioned, .everything), ]) let result = resolver.solve(constraints: dependencies) @@ -1110,7 +1216,6 @@ final class PubgrubTests: XCTestCase { ]) } - // top level package -> version // top level package -> non-versioned -> version func testHappyPath8() throws { @@ -1118,15 +1223,20 @@ final class PubgrubTests: XCTestCase { try builder.serve(package, at: .unversioned, with: [ "module": [ "config": (.versionSet(v1Range), .specific(["config"])), - "foo": (.unversioned, .specific(["foo"])) - ]]) - try builder.serve("foo", at: .unversioned, with: ["foo": ["config": (.versionSet(v1_1Range), .specific(["config"]))]]) + "foo": (.unversioned, .specific(["foo"])), + ], + ]) + try builder.serve( + "foo", + at: .unversioned, + with: ["foo": ["config": (.versionSet(v1_1Range), .specific(["config"]))]] + ) try builder.serve("config", at: v1) try builder.serve("config", at: v1_1) let resolver = builder.create() let dependencies = builder.create(dependencies: [ - package: (.unversioned, .everything) + package: (.unversioned, .everything), ]) let result = resolver.solve(constraints: dependencies) @@ -1145,17 +1255,22 @@ final class PubgrubTests: XCTestCase { try builder.serve(package, at: .unversioned, with: [ "module": [ "config": (.versionSet(v1Range), .specific(["config"])), - "foo": (.unversioned, .specific(["foo"])) - ]]) + "foo": (.unversioned, .specific(["foo"])), + ], + ]) try builder.serve("foo", at: .unversioned, with: ["foo": ["bar": (.unversioned, .specific(["bar"]))]]) try builder.serve("bar", at: .unversioned, with: ["bar": ["baz": (.unversioned, .specific(["baz"]))]]) - try builder.serve("baz", at: .unversioned, with: ["baz": ["config": (.versionSet(v1_1Range), .specific(["config"]))]]) + try builder.serve( + "baz", + at: .unversioned, + with: ["baz": ["config": (.versionSet(v1_1Range), .specific(["config"]))]] + ) try builder.serve("config", at: v1) try builder.serve("config", at: v1_1) let resolver = builder.create() let dependencies = builder.create(dependencies: [ - package: (.unversioned, .everything) + package: (.unversioned, .everything), ]) let result = resolver.solve(constraints: dependencies) @@ -1175,10 +1290,19 @@ final class PubgrubTests: XCTestCase { let package = PackageReference.root(identity: .plain("package"), path: .root) try builder.serve(package, at: .unversioned, with: [ "module": [ - "foo": (.versionSet(.range("1.0.0-alpha" ..< "2.0.0")), .specific(["foo"])) - ]]) - try builder.serve("foo", at: "1.0.0-alpha.1", with: ["foo": ["bar": (.versionSet(v1Range), .specific(["bar"]))]]) - try builder.serve("foo", at: "1.0.0-alpha.2", with: ["foo": ["bar": (.versionSet(v1Range), .specific(["bar"]))]]) + "foo": (.versionSet(.range("1.0.0-alpha" ..< "2.0.0")), .specific(["foo"])), + ], + ]) + try builder.serve( + "foo", + at: "1.0.0-alpha.1", + with: ["foo": ["bar": (.versionSet(v1Range), .specific(["bar"]))]] + ) + try builder.serve( + "foo", + at: "1.0.0-alpha.2", + with: ["foo": ["bar": (.versionSet(v1Range), .specific(["bar"]))]] + ) try builder.serve("foo", at: "1.0.0-beta.1", with: ["foo": ["bar": (.versionSet(v1Range), .specific(["bar"]))]]) try builder.serve("foo", at: "1.0.0-beta.2", with: ["foo": ["bar": (.versionSet(v1Range), .specific(["bar"]))]]) try builder.serve("foo", at: "1.0.0-beta.3", with: ["foo": ["bar": (.versionSet(v1Range), .specific(["bar"]))]]) @@ -1188,7 +1312,7 @@ final class PubgrubTests: XCTestCase { let resolver = builder.create() let dependencies = builder.create(dependencies: [ - package: (.unversioned, .everything) + package: (.unversioned, .everything), ]) let result = resolver.solve(constraints: dependencies) @@ -1196,29 +1320,37 @@ final class PubgrubTests: XCTestCase { AssertResult(result, [ ("package", .unversioned), ("foo", .version("1.0.0-beta.3")), - ("bar", .version(v1_5)) + ("bar", .version(v1_5)), ]) } func testResolutionWithSimpleBranchBasedDependency() throws { - try builder.serve("foo", at: .revision("master"), with: ["foo": ["bar": (.versionSet(v1Range), .specific(["bar"]))]]) + try builder.serve( + "foo", + at: .revision("master"), + with: ["foo": ["bar": (.versionSet(v1Range), .specific(["bar"]))]] + ) try builder.serve("bar", at: v1) let resolver = builder.create() let dependencies = try builder.create(dependencies: [ "foo": (.revision("master"), .specific(["foo"])), - "bar": (.versionSet(v1Range), .specific(["bar"])) + "bar": (.versionSet(v1Range), .specific(["bar"])), ]) let result = resolver.solve(constraints: dependencies) AssertResult(result, [ ("foo", .revision("master")), - ("bar", .version(v1)) + ("bar", .version(v1)), ]) } func testResolutionWithSimpleBranchBasedDependency2() throws { - try builder.serve("foo", at: .revision("master"), with: ["foo": ["bar": (.versionSet(v1Range), .specific(["bar"]))]]) + try builder.serve( + "foo", + at: .revision("master"), + with: ["foo": ["bar": (.versionSet(v1Range), .specific(["bar"]))]] + ) try builder.serve("bar", at: v1) let resolver = builder.create() @@ -1229,7 +1361,7 @@ final class PubgrubTests: XCTestCase { AssertResult(result, [ ("foo", .revision("master")), - ("bar", .version(v1)) + ("bar", .version(v1)), ]) } @@ -1247,7 +1379,7 @@ final class PubgrubTests: XCTestCase { AssertResult(result, [ ("foo", .revision("master")), - ("bar", .version(v1)) + ("bar", .version(v1)), ]) } @@ -1264,17 +1396,25 @@ final class PubgrubTests: XCTestCase { AssertResult(result, [ ("foo", .revision("master")), - ("bar", .version(v1)) + ("bar", .version(v1)), ]) } func testResolutionWithOverridingBranchBasedDependency3() throws { - try builder.serve("foo", at: .revision("master"), with: ["foo": ["bar": (.revision("master"), .specific(["bar"]))]]) + try builder.serve( + "foo", + at: .revision("master"), + with: ["foo": ["bar": (.revision("master"), .specific(["bar"]))]] + ) try builder.serve("bar", at: .revision("master")) try builder.serve("bar", at: v1) - try builder.serve("baz", at: .revision("master"), with: ["baz": ["bar": (.versionSet(v1Range), .specific(["bar"]))]]) + try builder.serve( + "baz", + at: .revision("master"), + with: ["baz": ["bar": (.versionSet(v1Range), .specific(["bar"]))]] + ) let resolver = builder.create() let dependencies = try builder.create(dependencies: [ @@ -1295,7 +1435,7 @@ final class PubgrubTests: XCTestCase { let resolver = builder.create() let dependencies = try builder.create(dependencies: [ - "foo": (.revision("master"), .specific(["foo"])) + "foo": (.revision("master"), .specific(["foo"])), ]) let result = resolver.solve(constraints: dependencies) @@ -1303,7 +1443,11 @@ final class PubgrubTests: XCTestCase { } func testResolutionWithRevisionConflict() throws { - try builder.serve("foo", at: .revision("master"), with: ["foo": ["bar": (.revision("master"), .specific(["bar"]))]]) + try builder.serve( + "foo", + at: .revision("master"), + with: ["foo": ["bar": (.revision("master"), .specific(["bar"]))]] + ) try builder.serve("bar", at: .version(v1)) try builder.serve("bar", at: .revision("master")) @@ -1341,7 +1485,7 @@ final class PubgrubTests: XCTestCase { AssertResult(result, [ ("swift-nio-ssl", .revision("master")), ("swift-nio", .revision("master")), - ("foo", .version(v1)) + ("foo", .version(v1)), ]) } @@ -1355,13 +1499,13 @@ final class PubgrubTests: XCTestCase { "nio-postgres": [ "swift-nio": (.revision("master"), .specific(["swift-nio"])), "swift-nio-ssl": (.revision("master"), .specific(["swift-nio-ssl"])), - ] + ], ]) try builder.serve("http-client", at: v1, with: [ "http-client": [ "swift-nio": (.versionSet(v1Range), .specific(["swift-nio"])), "boring-ssl": (.versionSet(v1Range), .specific(["boring-ssl"])), - ] + ], ]) try builder.serve("boring-ssl", at: v1, with: [ "boring-ssl": ["swift-nio": (.versionSet(v1Range), .specific(["swift-nio"]))], @@ -1386,7 +1530,7 @@ final class PubgrubTests: XCTestCase { func testNonVersionDependencyInVersionDependency2() throws { try builder.serve("foo", at: v1_1, with: [ - "foo": ["bar": (.revision("master"), .specific(["bar"]))] + "foo": ["bar": (.revision("master"), .specific(["bar"]))], ]) try builder.serve("foo", at: v1) let resolver = builder.create() @@ -1420,12 +1564,15 @@ final class PubgrubTests: XCTestCase { let result = try resolver.solve(root: rootNode, constraints: dependencies) // Since a was pinned, we shouldn't have computed bounds for its incomaptibilities. - let aIncompat = result.state.positiveIncompatibilities(for: .product("a", package: try builder.reference(for: "a")))![0] + let aIncompat = try result.state.positiveIncompatibilities(for: .product( + "a", + package: builder.reference(for: "a") + ))![0] XCTAssertEqual(aIncompat.terms[0].requirement, .exact("1.0.0")) AssertResult(Result.success(result.bindings), [ ("a", .version(v1)), - ("b", .version(v1)) + ("b", .version(v1)), ]) } @@ -1436,7 +1583,7 @@ final class PubgrubTests: XCTestCase { try builder.serve("a", at: v1_1) try builder.serve("b", at: v1) try builder.serve("b", at: v1_1) - try builder.serve("c", at: v1, with: ["c": ["b": (.versionSet(.range(v1_1..= 1.0.0 practically depends on 'baz' 3.0.0..<4.0.0. - 'bar' >= 2.0.0 practically depends on 'baz' 3.0.0..<4.0.0 because 'bar' 2.0.0 depends on 'baz' 3.0.0..<4.0.0 and no versions of 'bar' match the requirement 2.0.1..<3.0.0. - 'foo' >= 1.0.0 practically depends on 'bar' 2.0.0..<3.0.0 because 'foo' 1.0.0 depends on 'bar' 2.0.0..<3.0.0 and no versions of 'foo' match the requirement 1.0.1..<2.0.0. - """) + Dependencies could not be resolved because root depends on 'foo' 1.0.0..<2.0.0 and root depends on 'baz' 1.0.0..<2.0.0. + 'foo' >= 1.0.0 practically depends on 'baz' 3.0.0..<4.0.0. + 'bar' >= 2.0.0 practically depends on 'baz' 3.0.0..<4.0.0 because 'bar' 2.0.0 depends on 'baz' 3.0.0..<4.0.0 and no versions of 'bar' match the requirement 2.0.1..<3.0.0. + 'foo' >= 1.0.0 practically depends on 'bar' 2.0.0..<3.0.0 because 'foo' 1.0.0 depends on 'bar' 2.0.0..<3.0.0 and no versions of 'foo' match the requirement 1.0.1..<2.0.0. + """) } func testResolutionBranchingErrorReporting() throws { try builder.serve("foo", at: v1, with: [ "foo": [ "a": (.versionSet(v1Range), .specific(["a"])), - "b": (.versionSet(v1Range), .specific(["b"])) - ] + "b": (.versionSet(v1Range), .specific(["b"])), + ], ]) try builder.serve("foo", at: v1_1, with: [ "foo": [ "x": (.versionSet(v1Range), .specific(["x"])), - "y": (.versionSet(v1Range), .specific(["y"])) - ] + "y": (.versionSet(v1Range), .specific(["y"])), + ], ]) try builder.serve("a", at: v1, with: ["a": ["b": (.versionSet(v2Range), .specific(["b"]))]]) try builder.serve("b", at: v1) @@ -2047,14 +2303,14 @@ final class PubGrubDiagnosticsTests: XCTestCase { print(result.errorMsg!) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because root depends on 'foo' 1.0.0..<2.0.0. - 'foo' >= 1.0.0 cannot be used because 'foo' {1.0.0..<1.1.0, 1.1.1..<2.0.0} cannot be used (1). - 'foo' 1.1.0 cannot be used because 'foo' 1.1.0 depends on 'x' 1.0.0..<2.0.0 and 'foo' 1.1.0 depends on 'y' 1.0.0..<2.0.0. - 'x' >= 1.0.0 practically depends on 'y' 2.0.0..<3.0.0 because 'x' 1.0.0 depends on 'y' 2.0.0..<3.0.0 and no versions of 'x' match the requirement 1.0.1..<2.0.0. - 'foo' 1.0.0 practically depends on 'b' 2.0.0..<3.0.0 because 'foo' 1.0.0 depends on 'a' 1.0.0..<2.0.0. - 'a' >= 1.0.0 practically depends on 'b' 2.0.0..<3.0.0 because 'a' 1.0.0 depends on 'b' 2.0.0..<3.0.0 and no versions of 'a' match the requirement 1.0.1..<2.0.0. - (1) As a result, 'foo' {1.0.0..<1.1.0, 1.1.1..<2.0.0} cannot be used because 'foo' 1.0.0 depends on 'b' 1.0.0..<2.0.0 and no versions of 'foo' match the requirement {1.0.1..<1.1.0, 1.1.1..<2.0.0}. - """) + Dependencies could not be resolved because root depends on 'foo' 1.0.0..<2.0.0. + 'foo' >= 1.0.0 cannot be used because 'foo' {1.0.0..<1.1.0, 1.1.1..<2.0.0} cannot be used (1). + 'foo' 1.1.0 cannot be used because 'foo' 1.1.0 depends on 'x' 1.0.0..<2.0.0 and 'foo' 1.1.0 depends on 'y' 1.0.0..<2.0.0. + 'x' >= 1.0.0 practically depends on 'y' 2.0.0..<3.0.0 because 'x' 1.0.0 depends on 'y' 2.0.0..<3.0.0 and no versions of 'x' match the requirement 1.0.1..<2.0.0. + 'foo' 1.0.0 practically depends on 'b' 2.0.0..<3.0.0 because 'foo' 1.0.0 depends on 'a' 1.0.0..<2.0.0. + 'a' >= 1.0.0 practically depends on 'b' 2.0.0..<3.0.0 because 'a' 1.0.0 depends on 'b' 2.0.0..<3.0.0 and no versions of 'a' match the requirement 1.0.1..<2.0.0. + (1) As a result, 'foo' {1.0.0..<1.1.0, 1.1.1..<2.0.0} cannot be used because 'foo' 1.0.0 depends on 'b' 1.0.0..<2.0.0 and no versions of 'foo' match the requirement {1.0.1..<1.1.0, 1.1.1..<2.0.0}. + """) } func testConflict1() throws { @@ -2066,15 +2322,15 @@ final class PubGrubDiagnosticsTests: XCTestCase { let resolver = builder.create() let dependencies = try builder.create(dependencies: [ "foo": (.versionSet(v1Range), .specific(["foo"])), - "bar": (.versionSet(v1Range), .specific(["bar"])) + "bar": (.versionSet(v1Range), .specific(["bar"])), ]) let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because root depends on 'foo' 1.0.0..<2.0.0 and root depends on 'bar' 1.0.0..<2.0.0. - 'bar' is incompatible with 'foo' because 'foo' 1.0.0 depends on 'config' 1.0.0..<2.0.0 and no versions of 'foo' match the requirement 1.0.1..<2.0.0. - 'bar' >= 1.0.0 practically depends on 'config' 2.0.0..<3.0.0 because no versions of 'bar' match the requirement 1.0.1..<2.0.0 and 'bar' 1.0.0 depends on 'config' 2.0.0..<3.0.0. - """) + Dependencies could not be resolved because root depends on 'foo' 1.0.0..<2.0.0 and root depends on 'bar' 1.0.0..<2.0.0. + 'bar' is incompatible with 'foo' because 'foo' 1.0.0 depends on 'config' 1.0.0..<2.0.0 and no versions of 'foo' match the requirement 1.0.1..<2.0.0. + 'bar' >= 1.0.0 practically depends on 'config' 2.0.0..<3.0.0 because no versions of 'bar' match the requirement 1.0.1..<2.0.0 and 'bar' 1.0.0 depends on 'config' 2.0.0..<3.0.0. + """) } func testConflict2() throws { @@ -2093,9 +2349,9 @@ final class PubGrubDiagnosticsTests: XCTestCase { let result1 = resolver1.solve(constraints: dependencies1) XCTAssertEqual(result1.errorMsg, """ - Dependencies could not be resolved because root depends on 'config' 2.0.0..<3.0.0 and root depends on 'foo' 1.0.0..<2.0.0. - 'foo' >= 1.0.0 practically depends on 'config' 1.0.0..<2.0.0 because no versions of 'foo' match the requirement 1.0.1..<2.0.0 and 'foo' 1.0.0 depends on 'config' 1.0.0..<2.0.0. - """) + Dependencies could not be resolved because root depends on 'config' 2.0.0..<3.0.0 and root depends on 'foo' 1.0.0..<2.0.0. + 'foo' >= 1.0.0 practically depends on 'config' 1.0.0..<2.0.0 because no versions of 'foo' match the requirement 1.0.1..<2.0.0 and 'foo' 1.0.0 depends on 'config' 1.0.0..<2.0.0. + """) let dependencies2 = try builder.create(dependencies: [ "foo": (.versionSet(v1Range), .specific(["foo"])), @@ -2106,9 +2362,9 @@ final class PubGrubDiagnosticsTests: XCTestCase { let result2 = resolver2.solve(constraints: dependencies2) XCTAssertEqual(result2.errorMsg, """ - Dependencies could not be resolved because root depends on 'foo' 1.0.0..<2.0.0 and root depends on 'config' 2.0.0..<3.0.0. - 'foo' >= 1.0.0 practically depends on 'config' 1.0.0..<2.0.0 because 'foo' 1.0.0 depends on 'config' 1.0.0..<2.0.0 and no versions of 'foo' match the requirement 1.0.1..<2.0.0. - """) + Dependencies could not be resolved because root depends on 'foo' 1.0.0..<2.0.0 and root depends on 'config' 2.0.0..<3.0.0. + 'foo' >= 1.0.0 practically depends on 'config' 1.0.0..<2.0.0 because 'foo' 1.0.0 depends on 'config' 1.0.0..<2.0.0 and no versions of 'foo' match the requirement 1.0.1..<2.0.0. + """) } func testConflict3() throws { @@ -2123,16 +2379,16 @@ final class PubGrubDiagnosticsTests: XCTestCase { let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because no versions of 'config' match the requirement 2.0.0..<3.0.0 and root depends on 'config' 2.0.0..<3.0.0. - """) + Dependencies could not be resolved because no versions of 'config' match the requirement 2.0.0..<3.0.0 and root depends on 'config' 2.0.0..<3.0.0. + """) } func testConflict4() throws { try builder.serve("foo", at: v1, with: [ - "foo": ["shared": (.versionSet(.range("2.0.0"..<"3.0.0")), .specific(["shared"]))], + "foo": ["shared": (.versionSet(.range("2.0.0" ..< "3.0.0")), .specific(["shared"]))], ]) try builder.serve("bar", at: v1, with: [ - "bar": ["shared": (.versionSet(.range("2.9.0"..<"4.0.0")), .specific(["shared"]))], + "bar": ["shared": (.versionSet(.range("2.9.0" ..< "4.0.0")), .specific(["shared"]))], ]) try builder.serve("shared", at: "2.5.0") try builder.serve("shared", at: "3.5.0") @@ -2146,10 +2402,10 @@ final class PubGrubDiagnosticsTests: XCTestCase { let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because root depends on 'bar' 1.0.0 and root depends on 'foo' 1.0.0. - 'foo' is incompatible with 'bar' because 'foo' 1.0.0 depends on 'shared' 2.0.0..<3.0.0. - 'bar' 1.0.0 practically depends on 'shared' 3.0.0..<4.0.0 because 'bar' 1.0.0 depends on 'shared' 2.9.0..<4.0.0 and no versions of 'shared' match the requirement 2.9.0..<3.0.0. - """) + Dependencies could not be resolved because root depends on 'bar' 1.0.0 and root depends on 'foo' 1.0.0. + 'foo' is incompatible with 'bar' because 'foo' 1.0.0 depends on 'shared' 2.0.0..<3.0.0. + 'bar' 1.0.0 practically depends on 'shared' 3.0.0..<4.0.0 because 'bar' 1.0.0 depends on 'shared' 2.9.0..<4.0.0 and no versions of 'shared' match the requirement 2.9.0..<3.0.0. + """) } func testConflict5() throws { @@ -2168,19 +2424,19 @@ final class PubGrubDiagnosticsTests: XCTestCase { let resolver = builder.create() let dependencies = try builder.create(dependencies: [ - "b": (.versionSet(.range("0.0.0"..<"5.0.0")), .specific(["b"])), - "a": (.versionSet(.range("0.0.0"..<"5.0.0")), .specific(["a"])), + "b": (.versionSet(.range("0.0.0" ..< "5.0.0")), .specific(["b"])), + "a": (.versionSet(.range("0.0.0" ..< "5.0.0")), .specific(["a"])), ]) let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because root depends on 'a' 0.0.0..<5.0.0. - 'a' cannot be used. - 'a' 2.0.0 cannot be used because 'b' 2.0.0 depends on 'a' 1.0.0 and 'a' 2.0.0 depends on 'b' 2.0.0. - 'a' {0.0.0..<2.0.0, 2.0.1..<5.0.0} cannot be used because 'b' 1.0.0 depends on 'a' 2.0.0. - 'a' {0.0.0..<2.0.0, 2.0.1..<5.0.0} practically depends on 'b' 1.0.0 because no versions of 'a' match the requirement {0.0.0..<1.0.0, 1.0.1..<2.0.0, 2.0.1..<5.0.0} and 'a' 1.0.0 depends on 'b' 1.0.0. - """) + Dependencies could not be resolved because root depends on 'a' 0.0.0..<5.0.0. + 'a' cannot be used. + 'a' 2.0.0 cannot be used because 'b' 2.0.0 depends on 'a' 1.0.0 and 'a' 2.0.0 depends on 'b' 2.0.0. + 'a' {0.0.0..<2.0.0, 2.0.1..<5.0.0} cannot be used because 'b' 1.0.0 depends on 'a' 2.0.0. + 'a' {0.0.0..<2.0.0, 2.0.1..<5.0.0} practically depends on 'b' 1.0.0 because no versions of 'a' match the requirement {0.0.0..<1.0.0, 1.0.1..<2.0.0, 2.0.1..<5.0.0} and 'a' 1.0.0 depends on 'b' 1.0.0. + """) } // root -> version -> version @@ -2194,38 +2450,42 @@ final class PubGrubDiagnosticsTests: XCTestCase { let resolver = builder.create() let dependencies = try builder.create(dependencies: [ "foo": (.versionSet(v1Range), .specific(["foo"])), - "bar": (.versionSet(v1Range), .specific(["bar"])) + "bar": (.versionSet(v1Range), .specific(["bar"])), ]) let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because root depends on 'foo' 1.0.0..<2.0.0 and root depends on 'bar' 1.0.0..<2.0.0. - 'bar' is incompatible with 'foo' because 'foo' 1.0.0 depends on 'config' 1.0.0..<2.0.0 and no versions of 'foo' match the requirement 1.0.1..<2.0.0. - 'bar' >= 1.0.0 practically depends on 'config' 2.0.0..<3.0.0 because no versions of 'bar' match the requirement 1.0.1..<2.0.0 and 'bar' 1.0.0 depends on 'config' 2.0.0..<3.0.0. - """) + Dependencies could not be resolved because root depends on 'foo' 1.0.0..<2.0.0 and root depends on 'bar' 1.0.0..<2.0.0. + 'bar' is incompatible with 'foo' because 'foo' 1.0.0 depends on 'config' 1.0.0..<2.0.0 and no versions of 'foo' match the requirement 1.0.1..<2.0.0. + 'bar' >= 1.0.0 practically depends on 'config' 2.0.0..<3.0.0 because no versions of 'bar' match the requirement 1.0.1..<2.0.0 and 'bar' 1.0.0 depends on 'config' 2.0.0..<3.0.0. + """) } // root -> version -> version // root -> non-versioned -> conflicting version func testConflict7() throws { try builder.serve("foo", at: v1, with: ["foo": ["config": (.versionSet(v1Range), .specific(["config"]))]]) - try builder.serve("bar", at: .unversioned, with: ["bar": ["config": (.versionSet(v2Range), .specific(["config"]))]]) + try builder.serve( + "bar", + at: .unversioned, + with: ["bar": ["config": (.versionSet(v2Range), .specific(["config"]))]] + ) try builder.serve("config", at: v1) try builder.serve("config", at: v2) let resolver = builder.create() let dependencies = try builder.create(dependencies: [ "foo": (.versionSet(v1Range), .specific(["foo"])), - "bar": (.unversioned, .specific(["bar"])) + "bar": (.unversioned, .specific(["bar"])), ]) let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because 'bar' depends on 'config' 2.0.0..<3.0.0 and root depends on 'foo' 1.0.0..<2.0.0. - 'foo' >= 1.0.0 practically depends on 'config' 1.0.0..<2.0.0 because no versions of 'foo' match the requirement 1.0.1..<2.0.0 and 'foo' 1.0.0 depends on 'config' 1.0.0..<2.0.0. - """) + Dependencies could not be resolved because 'bar' depends on 'config' 2.0.0..<3.0.0 and root depends on 'foo' 1.0.0..<2.0.0. + 'foo' >= 1.0.0 practically depends on 'config' 1.0.0..<2.0.0 because no versions of 'foo' match the requirement 1.0.1..<2.0.0 and 'foo' 1.0.0 depends on 'config' 1.0.0..<2.0.0. + """) } // root -> version -> version @@ -2233,81 +2493,97 @@ final class PubGrubDiagnosticsTests: XCTestCase { func testConflict8() throws { try builder.serve("foo", at: v1, with: ["foo": ["config": (.versionSet(v1Range), .specific(["config"]))]]) try builder.serve("bar", at: .unversioned, with: ["bar": ["baz": (.unversioned, .specific(["baz"]))]]) - try builder.serve("baz", at: .unversioned, with: ["baz": ["config": (.versionSet(v2Range), .specific(["config"]))]]) + try builder.serve( + "baz", + at: .unversioned, + with: ["baz": ["config": (.versionSet(v2Range), .specific(["config"]))]] + ) try builder.serve("config", at: v1) try builder.serve("config", at: v2) let resolver = builder.create() let dependencies = try builder.create(dependencies: [ "foo": (.versionSet(v1Range), .specific(["foo"])), - "bar": (.unversioned, .specific(["bar"])) + "bar": (.unversioned, .specific(["bar"])), ]) let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because 'baz' depends on 'config' 2.0.0..<3.0.0 and root depends on 'foo' 1.0.0..<2.0.0. - 'foo' >= 1.0.0 practically depends on 'config' 1.0.0..<2.0.0 because no versions of 'foo' match the requirement 1.0.1..<2.0.0 and 'foo' 1.0.0 depends on 'config' 1.0.0..<2.0.0. - """) + Dependencies could not be resolved because 'baz' depends on 'config' 2.0.0..<3.0.0 and root depends on 'foo' 1.0.0..<2.0.0. + 'foo' >= 1.0.0 practically depends on 'config' 1.0.0..<2.0.0 because no versions of 'foo' match the requirement 1.0.1..<2.0.0 and 'foo' 1.0.0 depends on 'config' 1.0.0..<2.0.0. + """) } // root -> version -> version // root -> non-versioned -> non-existing version func testConflict9() throws { try builder.serve("foo", at: v1, with: ["foo": ["config": (.versionSet(v1Range), .specific(["config"]))]]) - try builder.serve("bar", at: .unversioned, with: ["bar": ["config": (.versionSet(v2Range), .specific(["config"]))]]) + try builder.serve( + "bar", + at: .unversioned, + with: ["bar": ["config": (.versionSet(v2Range), .specific(["config"]))]] + ) try builder.serve("config", at: v1) let resolver = builder.create() let dependencies = try builder.create(dependencies: [ "foo": (.versionSet(v1Range), .specific(["foo"])), - "bar": (.unversioned, .specific(["bar"])) + "bar": (.unversioned, .specific(["bar"])), ]) let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because no versions of 'config' match the requirement 2.0.0..<3.0.0 and 'bar' depends on 'config' 2.0.0..<3.0.0. - """) + Dependencies could not be resolved because no versions of 'config' match the requirement 2.0.0..<3.0.0 and 'bar' depends on 'config' 2.0.0..<3.0.0. + """) } // root -> version // root -> non-versioned -> conflicting version func testConflict10() throws { - try builder.serve("foo", at: .unversioned, with: ["foo": ["config": (.versionSet(v2Range), .specific(["config"]))]]) + try builder.serve( + "foo", + at: .unversioned, + with: ["foo": ["config": (.versionSet(v2Range), .specific(["config"]))]] + ) try builder.serve("config", at: v1) try builder.serve("config", at: v2) let resolver = builder.create() let dependencies = try builder.create(dependencies: [ "config": (.versionSet(v1Range), .specific(["config"])), - "foo": (.unversioned, .specific(["foo"])) + "foo": (.unversioned, .specific(["foo"])), ]) let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because 'foo' depends on 'config' 2.0.0..<3.0.0 and root depends on 'config' 1.0.0..<2.0.0. - """) + Dependencies could not be resolved because 'foo' depends on 'config' 2.0.0..<3.0.0 and root depends on 'config' 1.0.0..<2.0.0. + """) } // root -> version // root -> non-versioned -> non-existing version func testConflict11() throws { - try builder.serve("foo", at: .unversioned, with: ["foo": ["config": (.versionSet(v2Range), .specific(["config"]))]]) + try builder.serve( + "foo", + at: .unversioned, + with: ["foo": ["config": (.versionSet(v2Range), .specific(["config"]))]] + ) try builder.serve("config", at: v1) let resolver = builder.create() let dependencies = try builder.create(dependencies: [ "config": (.versionSet(v1Range), .specific(["config"])), - "foo": (.unversioned, .specific(["foo"])) + "foo": (.unversioned, .specific(["foo"])), ]) let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because 'foo' depends on 'config' 2.0.0..<3.0.0 and root depends on 'config' 1.0.0..<2.0.0. - """) + Dependencies could not be resolved because 'foo' depends on 'config' 2.0.0..<3.0.0 and root depends on 'config' 1.0.0..<2.0.0. + """) } // root -> version @@ -2315,20 +2591,24 @@ final class PubGrubDiagnosticsTests: XCTestCase { func testConflict12() throws { try builder.serve("foo", at: .unversioned, with: ["foo": ["bar": (.unversioned, .specific(["bar"]))]]) try builder.serve("bar", at: .unversioned, with: ["bar": ["baz": (.unversioned, .specific(["baz"]))]]) - try builder.serve("baz", at: .unversioned, with: ["baz": ["config": (.versionSet(v2Range), .specific(["config"]))]]) + try builder.serve( + "baz", + at: .unversioned, + with: ["baz": ["config": (.versionSet(v2Range), .specific(["config"]))]] + ) try builder.serve("config", at: v1) let resolver = builder.create() let dependencies = try builder.create(dependencies: [ "config": (.versionSet(v1Range), .specific(["config"])), - "foo": (.unversioned, .specific(["foo"])) + "foo": (.unversioned, .specific(["foo"])), ]) let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because 'baz' depends on 'config' 2.0.0..<3.0.0 and root depends on 'config' 1.0.0..<2.0.0. - """) + Dependencies could not be resolved because 'baz' depends on 'config' 2.0.0..<3.0.0 and root depends on 'config' 1.0.0..<2.0.0. + """) } // top level package -> version @@ -2338,23 +2618,24 @@ final class PubGrubDiagnosticsTests: XCTestCase { try builder.serve(package, at: .unversioned, with: [ "module": [ "config": (.versionSet(v1Range), .specific(["config"])), - "foo": (.versionSet(v1Range), .specific(["foo"])) - ]]) + "foo": (.versionSet(v1Range), .specific(["foo"])), + ], + ]) try builder.serve("foo", at: v1, with: ["foo": ["config": (.versionSet(v2Range), .specific(["config"]))]]) try builder.serve("config", at: v1) try builder.serve("config", at: v2) let resolver = builder.create() let dependencies = builder.create(dependencies: [ - package: (.unversioned, .everything) + package: (.unversioned, .everything), ]) let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because root depends on 'config' 1.0.0..<2.0.0 and root depends on 'foo' 1.0.0..<2.0.0. - 'foo' >= 1.0.0 practically depends on 'config' 2.0.0..<3.0.0 because no versions of 'foo' match the requirement 1.0.1..<2.0.0 and 'foo' 1.0.0 depends on 'config' 2.0.0..<3.0.0. - """) + Dependencies could not be resolved because root depends on 'config' 1.0.0..<2.0.0 and root depends on 'foo' 1.0.0..<2.0.0. + 'foo' >= 1.0.0 practically depends on 'config' 2.0.0..<3.0.0 because no versions of 'foo' match the requirement 1.0.1..<2.0.0 and 'foo' 1.0.0 depends on 'config' 2.0.0..<3.0.0. + """) } // top level package -> version @@ -2364,22 +2645,23 @@ final class PubGrubDiagnosticsTests: XCTestCase { try builder.serve(package, at: .unversioned, with: [ "module": [ "config": (.versionSet(v1Range), .specific(["config"])), - "foo": (.versionSet(v1Range), .specific(["foo"])) - ]]) + "foo": (.versionSet(v1Range), .specific(["foo"])), + ], + ]) try builder.serve("foo", at: v1, with: ["foo": ["config": (.versionSet(v2Range), .specific(["config"]))]]) try builder.serve("config", at: v1) let resolver = builder.create() let dependencies = builder.create(dependencies: [ - package: (.unversioned, .everything) + package: (.unversioned, .everything), ]) let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because root depends on 'config' 1.0.0..<2.0.0 and root depends on 'foo' 1.0.0..<2.0.0. - 'foo' >= 1.0.0 practically depends on 'config' 2.0.0..<3.0.0 because no versions of 'foo' match the requirement 1.0.1..<2.0.0 and 'foo' 1.0.0 depends on 'config' 2.0.0..<3.0.0. - """) + Dependencies could not be resolved because root depends on 'config' 1.0.0..<2.0.0 and root depends on 'foo' 1.0.0..<2.0.0. + 'foo' >= 1.0.0 practically depends on 'config' 2.0.0..<3.0.0 because no versions of 'foo' match the requirement 1.0.1..<2.0.0 and 'foo' 1.0.0 depends on 'config' 2.0.0..<3.0.0. + """) } // top level package -> version @@ -2389,22 +2671,27 @@ final class PubGrubDiagnosticsTests: XCTestCase { try builder.serve(package, at: .unversioned, with: [ "module": [ "config": (.versionSet(v1Range), .specific(["config"])), - "foo": (.unversioned, .specific(["foo"])) - ]]) - try builder.serve("foo", at: .unversioned, with: ["foo": ["config": (.versionSet(v2Range), .specific(["config"]))]]) + "foo": (.unversioned, .specific(["foo"])), + ], + ]) + try builder.serve( + "foo", + at: .unversioned, + with: ["foo": ["config": (.versionSet(v2Range), .specific(["config"]))]] + ) try builder.serve("config", at: v1) try builder.serve("config", at: v2) let resolver = builder.create() let dependencies = builder.create(dependencies: [ - package: (.unversioned, .everything) + package: (.unversioned, .everything), ]) let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because root depends on 'config' 1.0.0..<2.0.0 and 'foo' depends on 'config' 2.0.0..<3.0.0. - """) + Dependencies could not be resolved because root depends on 'config' 1.0.0..<2.0.0 and 'foo' depends on 'config' 2.0.0..<3.0.0. + """) } // top level package -> version @@ -2414,21 +2701,26 @@ final class PubGrubDiagnosticsTests: XCTestCase { try builder.serve(package, at: .unversioned, with: [ "module": [ "config": (.versionSet(v1Range), .specific(["config"])), - "foo": (.unversioned, .specific(["foo"])) - ]]) - try builder.serve("foo", at: .unversioned, with: ["foo": ["config": (.versionSet(v2Range), .specific(["config"]))]]) + "foo": (.unversioned, .specific(["foo"])), + ], + ]) + try builder.serve( + "foo", + at: .unversioned, + with: ["foo": ["config": (.versionSet(v2Range), .specific(["config"]))]] + ) try builder.serve("config", at: v1) let resolver = builder.create() let dependencies = builder.create(dependencies: [ - package: (.unversioned, .everything) + package: (.unversioned, .everything), ]) let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because root depends on 'config' 1.0.0..<2.0.0 and 'foo' depends on 'config' 2.0.0..<3.0.0. - """) + Dependencies could not be resolved because root depends on 'config' 1.0.0..<2.0.0 and 'foo' depends on 'config' 2.0.0..<3.0.0. + """) } // top level package -> version @@ -2438,49 +2730,65 @@ final class PubGrubDiagnosticsTests: XCTestCase { try builder.serve(package, at: .unversioned, with: [ "module": [ "config": (.versionSet(v1Range), .specific(["config"])), - "foo": (.unversioned, .specific(["foo"])) - ]]) + "foo": (.unversioned, .specific(["foo"])), + ], + ]) try builder.serve("foo", at: .unversioned, with: ["foo": ["bar": (.unversioned, .specific(["bar"]))]]) try builder.serve("bar", at: .unversioned, with: ["bar": ["baz": (.unversioned, .specific(["baz"]))]]) - try builder.serve("baz", at: .unversioned, with: ["baz": ["config": (.versionSet(v2Range), .specific(["config"]))]]) + try builder.serve( + "baz", + at: .unversioned, + with: ["baz": ["config": (.versionSet(v2Range), .specific(["config"]))]] + ) try builder.serve("config", at: v1) try builder.serve("config", at: v2) let resolver = builder.create() let dependencies = builder.create(dependencies: [ - package: (.unversioned, .everything) + package: (.unversioned, .everything), ]) let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because root depends on 'config' 1.0.0..<2.0.0 and 'baz' depends on 'config' 2.0.0..<3.0.0. - """) + Dependencies could not be resolved because root depends on 'config' 1.0.0..<2.0.0 and 'baz' depends on 'config' 2.0.0..<3.0.0. + """) } func testUnversioned6() throws { try builder.serve("foo", at: .unversioned) try builder.serve("bar", at: .revision("master"), with: [ - "bar": ["foo": (.unversioned, .specific(["foo"]))] + "bar": ["foo": (.unversioned, .specific(["foo"]))], ]) let resolver = builder.create() let dependencies = try builder.create(dependencies: [ - "bar": (.revision("master"), .specific(["bar"])) + "bar": (.revision("master"), .specific(["bar"])), ]) let result = resolver.solve(constraints: dependencies) - XCTAssertEqual(result.errorMsg, "package 'bar' is required using a revision-based requirement and it depends on local package 'foo', which is not supported") + XCTAssertEqual( + result.errorMsg, + "package 'bar' is required using a revision-based requirement and it depends on local package 'foo', which is not supported" + ) } func testResolutionWithOverridingBranchBasedDependency4() throws { - try builder.serve("foo", at: .revision("master"), with: ["foo": ["bar": (.revision("master"), .specific(["bar"]))]]) + try builder.serve( + "foo", + at: .revision("master"), + with: ["foo": ["bar": (.revision("master"), .specific(["bar"]))]] + ) try builder.serve("bar", at: .revision("master")) try builder.serve("bar", at: v1) - try builder.serve("baz", at: .revision("master"), with: ["baz": ["bar": (.revision("develop"), .specific(["baz"]))]]) + try builder.serve( + "baz", + at: .revision("master"), + with: ["baz": ["bar": (.revision("develop"), .specific(["baz"]))]] + ) let resolver = builder.create() let dependencies = try builder.create(dependencies: [ @@ -2489,12 +2797,15 @@ final class PubGrubDiagnosticsTests: XCTestCase { ]) let result = resolver.solve(constraints: dependencies) - XCTAssertEqual(result.errorMsg, "bar is required using two different revision-based requirements (master and develop), which is not supported") + XCTAssertEqual( + result.errorMsg, + "bar is required using two different revision-based requirements (master and develop), which is not supported" + ) } func testNonVersionDependencyInVersionDependency1() throws { try builder.serve("foo", at: v1_1, with: [ - "foo": ["bar": (.revision("master"), .specific(["bar"]))] + "foo": ["bar": (.revision("master"), .specific(["bar"]))], ]) try builder.serve("bar", at: .revision("master")) @@ -2505,14 +2816,14 @@ final class PubGrubDiagnosticsTests: XCTestCase { let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because root depends on 'foo' 1.0.0..<2.0.0. - 'foo' cannot be used because no versions of 'foo' match the requirement {1.0.0..<1.1.0, 1.1.1..<2.0.0} and package 'foo' is required using a stable-version but 'foo' depends on an unstable-version package 'bar'. - """) + Dependencies could not be resolved because root depends on 'foo' 1.0.0..<2.0.0. + 'foo' cannot be used because no versions of 'foo' match the requirement {1.0.0..<1.1.0, 1.1.1..<2.0.0} and package 'foo' is required using a stable-version but 'foo' depends on an unstable-version package 'bar'. + """) } func testNonVersionDependencyInVersionDependency2() throws { try builder.serve("foo", at: v1, with: [ - "foo": ["bar": (.unversioned, .specific(["bar"]))] + "foo": ["bar": (.unversioned, .specific(["bar"]))], ]) try builder.serve("bar", at: .unversioned) @@ -2523,19 +2834,19 @@ final class PubGrubDiagnosticsTests: XCTestCase { let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because package 'foo' is required using a stable-version but 'foo' depends on an unstable-version package 'bar' and root depends on 'foo' 1.0.0. - """) + Dependencies could not be resolved because package 'foo' is required using a stable-version but 'foo' depends on an unstable-version package 'bar' and root depends on 'foo' 1.0.0. + """) } func testNonVersionDependencyInVersionDependency3() throws { try builder.serve("foo", at: "1.0.0-beta.1", with: [ - "foo": ["bar": (.revision("master"), .specific(["bar"]))] + "foo": ["bar": (.revision("master"), .specific(["bar"]))], ]) try builder.serve("foo", at: "1.0.0-beta.2", with: [ - "foo": ["bar": (.revision("master"), .specific(["bar"]))] + "foo": ["bar": (.revision("master"), .specific(["bar"]))], ]) try builder.serve("foo", at: "1.0.0-beta.3", with: [ - "foo": ["bar": (.revision("master"), .specific(["bar"]))] + "foo": ["bar": (.revision("master"), .specific(["bar"]))], ]) try builder.serve("bar", at: .revision("master")) @@ -2546,10 +2857,10 @@ final class PubGrubDiagnosticsTests: XCTestCase { let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because package 'foo' is required using a stable-version but 'foo' depends on an unstable-version package 'bar' and root depends on 'foo' 1.0.0-beta..<2.0.0. - 'foo' {1.0.0-beta..<1.0.0-beta.3, 1.0.0-beta.3.0..<2.0.0} cannot be used because package 'foo' is required using a stable-version but 'foo' depends on an unstable-version package 'bar'. - 'foo' {1.0.0-beta..<1.0.0-beta.2, 1.0.0-beta.2.0..<1.0.0-beta.3, 1.0.0-beta.3.0..<2.0.0} cannot be used because no versions of 'foo' match the requirement {1.0.0-beta..<1.0.0-beta.1, 1.0.0-beta.1.0..<1.0.0-beta.2, 1.0.0-beta.2.0..<1.0.0-beta.3, 1.0.0-beta.3.0..<2.0.0} and package 'foo' is required using a stable-version but 'foo' depends on an unstable-version package 'bar'. - """) + Dependencies could not be resolved because package 'foo' is required using a stable-version but 'foo' depends on an unstable-version package 'bar' and root depends on 'foo' 1.0.0-beta..<2.0.0. + 'foo' {1.0.0-beta..<1.0.0-beta.3, 1.0.0-beta.3.0..<2.0.0} cannot be used because package 'foo' is required using a stable-version but 'foo' depends on an unstable-version package 'bar'. + 'foo' {1.0.0-beta..<1.0.0-beta.2, 1.0.0-beta.2.0..<1.0.0-beta.3, 1.0.0-beta.3.0..<2.0.0} cannot be used because no versions of 'foo' match the requirement {1.0.0-beta..<1.0.0-beta.1, 1.0.0-beta.1.0..<1.0.0-beta.2, 1.0.0-beta.2.0..<1.0.0-beta.3, 1.0.0-beta.3.0..<2.0.0} and package 'foo' is required using a stable-version but 'foo' depends on an unstable-version package 'bar'. + """) } func testIncompatibleToolsVersion1() throws { @@ -2563,14 +2874,17 @@ final class PubGrubDiagnosticsTests: XCTestCase { let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because root depends on 'a' 1.0.0..<2.0.0. - 'a' >= 1.0.0 cannot be used because no versions of 'a' match the requirement 1.0.1..<2.0.0 and 'a' 1.0.0 contains incompatible tools version (\(ToolsVersion.v5)). - """) + Dependencies could not be resolved because root depends on 'a' 1.0.0..<2.0.0. + 'a' >= 1.0.0 cannot be used because no versions of 'a' match the requirement 1.0.1..<2.0.0 and 'a' 1.0.0 contains incompatible tools version (\( + ToolsVersion + .v5 + )). + """) } func testIncompatibleToolsVersion3() throws { try builder.serve("a", at: v1_1, with: [ - "a": ["b": (.versionSet(v1Range), .specific(["b"]))] + "a": ["b": (.versionSet(v1Range), .specific(["b"]))], ]) try builder.serve("a", at: v1, toolsVersion: .v4) @@ -2586,10 +2900,13 @@ final class PubGrubDiagnosticsTests: XCTestCase { let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because root depends on 'a' 1.0.0..<2.0.0 and root depends on 'b' 2.0.0..<3.0.0. - 'a' >= 1.0.0 practically depends on 'b' 1.0.0..<2.0.0 because 'a' 1.1.0 depends on 'b' 1.0.0..<2.0.0. - 'a' {1.0.0..<1.1.0, 1.1.1..<2.0.0} cannot be used because no versions of 'a' match the requirement {1.0.1..<1.1.0, 1.1.1..<2.0.0} and 'a' 1.0.0 contains incompatible tools version (\(ToolsVersion.v4)). - """) + Dependencies could not be resolved because root depends on 'a' 1.0.0..<2.0.0 and root depends on 'b' 2.0.0..<3.0.0. + 'a' >= 1.0.0 practically depends on 'b' 1.0.0..<2.0.0 because 'a' 1.1.0 depends on 'b' 1.0.0..<2.0.0. + 'a' {1.0.0..<1.1.0, 1.1.1..<2.0.0} cannot be used because no versions of 'a' match the requirement {1.0.1..<1.1.0, 1.1.1..<2.0.0} and 'a' 1.0.0 contains incompatible tools version (\( + ToolsVersion + .v4 + )). + """) } func testIncompatibleToolsVersion4() throws { @@ -2599,14 +2916,17 @@ final class PubGrubDiagnosticsTests: XCTestCase { let resolver = builder.create() let dependencies = try builder.create(dependencies: [ - "a": (.versionSet(.range("3.2.0"..<"4.0.0")), .specific(["a"])), + "a": (.versionSet(.range("3.2.0" ..< "4.0.0")), .specific(["a"])), ]) let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because 'a' contains incompatible tools version (\(ToolsVersion.v3)) and root depends on 'a' 3.2.0..<4.0.0. - """) + Dependencies could not be resolved because 'a' contains incompatible tools version (\( + ToolsVersion + .v3 + )) and root depends on 'a' 3.2.0..<4.0.0. + """) } func testIncompatibleToolsVersion5() throws { @@ -2616,14 +2936,17 @@ final class PubGrubDiagnosticsTests: XCTestCase { let resolver = builder.create() let dependencies = try builder.create(dependencies: [ - "a": (.versionSet(.range("3.2.0"..<"4.0.0")), .specific(["a"])), + "a": (.versionSet(.range("3.2.0" ..< "4.0.0")), .specific(["a"])), ]) let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because 'a' contains incompatible tools version (\(ToolsVersion.v5)) and root depends on 'a' 3.2.0..<4.0.0. - """) + Dependencies could not be resolved because 'a' contains incompatible tools version (\( + ToolsVersion + .v5 + )) and root depends on 'a' 3.2.0..<4.0.0. + """) } func testIncompatibleToolsVersion6() throws { @@ -2636,47 +2959,53 @@ final class PubGrubDiagnosticsTests: XCTestCase { let resolver = builder.create() let dependencies = try builder.create(dependencies: [ - "a": (.versionSet(.range("3.2.0"..<"4.0.0")), .specific(["a"])), + "a": (.versionSet(.range("3.2.0" ..< "4.0.0")), .specific(["a"])), ]) let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because 'a' >= 3.2.1 contains incompatible tools version (\(ToolsVersion.v4)) and root depends on 'a' 3.2.0..<4.0.0. - 'a' 3.2.0 cannot be used because 'a' 3.2.0 depends on 'b' 1.0.0..<2.0.0. - 'b' >= 1.0.0 cannot be used because 'b' 1.0.0 contains incompatible tools version (\(ToolsVersion.v3)) and no versions of 'b' match the requirement 1.0.1..<2.0.0. - """) + Dependencies could not be resolved because 'a' >= 3.2.1 contains incompatible tools version (\( + ToolsVersion + .v4 + )) and root depends on 'a' 3.2.0..<4.0.0. + 'a' 3.2.0 cannot be used because 'a' 3.2.0 depends on 'b' 1.0.0..<2.0.0. + 'b' >= 1.0.0 cannot be used because 'b' 1.0.0 contains incompatible tools version (\( + ToolsVersion + .v3 + )) and no versions of 'b' match the requirement 1.0.1..<2.0.0. + """) } func testProductsCannotResolveToDifferentVersions() throws { try builder.serve("package", at: .unversioned, with: [ "package": [ "intermediate_a": (.versionSet(v1Range), .specific(["Intermediate A"])), - "intermediate_b": (.versionSet(v1Range), .specific(["Intermediate B"])) - ] + "intermediate_b": (.versionSet(v1Range), .specific(["Intermediate B"])), + ], ]) try builder.serve("intermediate_a", at: v1, with: [ "Intermediate A": [ - "transitive": (.versionSet(.exact(v1)), .specific(["Product A"])) - ] + "transitive": (.versionSet(.exact(v1)), .specific(["Product A"])), + ], ]) try builder.serve("intermediate_b", at: v1, with: [ "Intermediate B": [ - "transitive": (.versionSet(.exact(v1_1)), .specific(["Product B"])) - ] + "transitive": (.versionSet(.exact(v1_1)), .specific(["Product B"])), + ], ]) try builder.serve("transitive", at: v1, with: [ "Product A": [:], - "Product B": [:] + "Product B": [:], ]) try builder.serve("transitive", at: v1_1, with: [ "Product A": [:], - "Product B": [:] + "Product B": [:], ]) let resolver = builder.create() let dependencies = try builder.create(dependencies: [ - "package": (.unversioned, .everything) + "package": (.unversioned, .everything), ]) let result = resolver.solve(constraints: dependencies) @@ -2705,7 +3034,7 @@ final class PubGrubBacktrackTests: XCTestCase { let resolver = builder.create() let dependencies = try builder.create(dependencies: [ - "a": (.versionSet(.range("1.0.0"..<"3.0.0")), .specific(["a"])), + "a": (.versionSet(.range("1.0.0" ..< "3.0.0")), .specific(["a"])), ]) let result = resolver.solve(constraints: dependencies) @@ -2718,14 +3047,14 @@ final class PubGrubBacktrackTests: XCTestCase { func testBacktrack2() throws { try builder.serve("a", at: v1) try builder.serve("a", at: "2.0.0", with: [ - "a": ["c": (.versionSet(.range("1.0.0"..<"2.0.0")), .specific(["c"]))], + "a": ["c": (.versionSet(.range("1.0.0" ..< "2.0.0")), .specific(["c"]))], ]) try builder.serve("b", at: "1.0.0", with: [ - "b": ["c": (.versionSet(.range("2.0.0"..<"3.0.0")), .specific(["c"]))], + "b": ["c": (.versionSet(.range("2.0.0" ..< "3.0.0")), .specific(["c"]))], ]) try builder.serve("b", at: "2.0.0", with: [ - "b": ["c": (.versionSet(.range("3.0.0"..<"4.0.0")), .specific(["c"]))], + "b": ["c": (.versionSet(.range("3.0.0" ..< "4.0.0")), .specific(["c"]))], ]) try builder.serve("c", at: "1.0.0") @@ -2734,8 +3063,8 @@ final class PubGrubBacktrackTests: XCTestCase { let resolver = builder.create() let dependencies = try builder.create(dependencies: [ - "a": (.versionSet(.range("1.0.0"..<"3.0.0")), .specific(["a"])), - "b": (.versionSet(.range("1.0.0"..<"3.0.0")), .specific(["b"])), + "a": (.versionSet(.range("1.0.0" ..< "3.0.0")), .specific(["a"])), + "b": (.versionSet(.range("1.0.0" ..< "3.0.0")), .specific(["b"])), ]) let result = resolver.solve(constraints: dependencies) @@ -2749,18 +3078,18 @@ final class PubGrubBacktrackTests: XCTestCase { func testBacktrack3() throws { try builder.serve("a", at: "1.0.0", with: [ - "a": ["x": (.versionSet(.range("1.0.0"..<"5.0.0")), .specific(["x"]))], + "a": ["x": (.versionSet(.range("1.0.0" ..< "5.0.0")), .specific(["x"]))], ]) try builder.serve("b", at: "1.0.0", with: [ - "b": ["x": (.versionSet(.range("0.0.0"..<"2.0.0")), .specific(["x"]))], + "b": ["x": (.versionSet(.range("0.0.0" ..< "2.0.0")), .specific(["x"]))], ]) try builder.serve("c", at: "1.0.0") try builder.serve("c", at: "2.0.0", with: [ "c": [ - "a": (.versionSet(.range("0.0.0"..<"5.0.0")), .specific(["a"])), - "b": (.versionSet(.range("0.0.0"..<"5.0.0")), .specific(["b"])), - ] + "a": (.versionSet(.range("0.0.0" ..< "5.0.0")), .specific(["a"])), + "b": (.versionSet(.range("0.0.0" ..< "5.0.0")), .specific(["b"])), + ], ]) try builder.serve("x", at: "0.0.0") @@ -2774,8 +3103,8 @@ final class PubGrubBacktrackTests: XCTestCase { let resolver = builder.create() let dependencies = try builder.create(dependencies: [ - "c": (.versionSet(.range("1.0.0"..<"3.0.0")), .specific(["c"])), - "y": (.versionSet(.range("2.0.0"..<"3.0.0")), .specific(["y"])), + "c": (.versionSet(.range("1.0.0" ..< "3.0.0")), .specific(["c"])), + "y": (.versionSet(.range("2.0.0" ..< "3.0.0")), .specific(["y"])), ]) let result = resolver.solve(constraints: dependencies) @@ -2788,18 +3117,18 @@ final class PubGrubBacktrackTests: XCTestCase { func testBacktrack4() throws { try builder.serve("a", at: "1.0.0", with: [ - "a": ["x": (.versionSet(.range("1.0.0"..<"5.0.0")), .specific(["x"]))], + "a": ["x": (.versionSet(.range("1.0.0" ..< "5.0.0")), .specific(["x"]))], ]) try builder.serve("b", at: "1.0.0", with: [ - "b": ["x": (.versionSet(.range("0.0.0"..<"2.0.0")), .specific(["x"]))], + "b": ["x": (.versionSet(.range("0.0.0" ..< "2.0.0")), .specific(["x"]))], ]) try builder.serve("c", at: "1.0.0") try builder.serve("c", at: "2.0.0", with: [ "c": [ - "a": (.versionSet(.range("0.0.0"..<"5.0.0")), .specific(["a"])), - "b": (.versionSet(.range("0.0.0"..<"5.0.0")), .specific(["b"])), - ] + "a": (.versionSet(.range("0.0.0" ..< "5.0.0")), .specific(["a"])), + "b": (.versionSet(.range("0.0.0" ..< "5.0.0")), .specific(["b"])), + ], ]) try builder.serve("x", at: "0.0.0") @@ -2813,8 +3142,8 @@ final class PubGrubBacktrackTests: XCTestCase { let resolver = builder.create() let dependencies = try builder.create(dependencies: [ - "c": (.versionSet(.range("1.0.0"..<"3.0.0")), .specific(["c"])), - "y": (.versionSet(.range("2.0.0"..<"3.0.0")), .specific(["y"])), + "c": (.versionSet(.range("1.0.0" ..< "3.0.0")), .specific(["c"])), + "y": (.versionSet(.range("2.0.0" ..< "3.0.0")), .specific(["y"])), ]) let result = resolver.solve(constraints: dependencies) @@ -2837,7 +3166,7 @@ final class PubGrubBacktrackTests: XCTestCase { ]) try builder.serve("bar", at: "1.0.0", with: [ - "bar": ["baz": (.versionSet(.range("0.0.0"..<"3.0.0")), .specific(["baz"]))], + "bar": ["baz": (.versionSet(.range("0.0.0" ..< "3.0.0")), .specific(["baz"]))], ]) try builder.serve("bar", at: "2.0.0", with: [ "bar": ["baz": (.versionSet(.exact("3.0.0")), .specific(["baz"]))], @@ -2850,7 +3179,7 @@ final class PubGrubBacktrackTests: XCTestCase { let resolver = builder.create() let dependencies = try builder.create(dependencies: [ - "foo": (.versionSet(.range("1.0.0"..<"4.0.0")), .specific(["foo"])), + "foo": (.versionSet(.range("1.0.0" ..< "4.0.0")), .specific(["foo"])), ]) let result = resolver.solve(constraints: dependencies) @@ -2869,16 +3198,16 @@ final class PubGrubBacktrackTests: XCTestCase { "b": ["a": (.versionSet(.exact("1.0.0")), .specific(["a"]))], ]) try builder.serve("c", at: "1.0.0", with: [ - "c": ["b": (.versionSet(.range("0.0.0"..<"3.0.0")), .specific(["b"]))], + "c": ["b": (.versionSet(.range("0.0.0" ..< "3.0.0")), .specific(["b"]))], ]) try builder.serve("d", at: "1.0.0") try builder.serve("d", at: "2.0.0") let resolver = builder.create() let dependencies = try builder.create(dependencies: [ - "a": (.versionSet(.range("1.0.0"..<"4.0.0")), .specific(["a"])), - "c": (.versionSet(.range("1.0.0"..<"4.0.0")), .specific(["c"])), - "d": (.versionSet(.range("1.0.0"..<"4.0.0")), .specific(["d"])), + "a": (.versionSet(.range("1.0.0" ..< "4.0.0")), .specific(["a"])), + "c": (.versionSet(.range("1.0.0" ..< "4.0.0")), .specific(["c"])), + "d": (.versionSet(.range("1.0.0" ..< "4.0.0")), .specific(["d"])), ]) let result = resolver.solve(constraints: dependencies) @@ -2898,18 +3227,21 @@ final class PubGrubBacktrackTests: XCTestCase { "b": ["a": (.versionSet(.exact("1.0.0")), .specific(["a"]))], ]) try builder.serve("c", at: "1.5.2", with: [ - "c": ["b": (.versionSet(.range("0.0.0"..<"3.0.0")), .specific(["b"]))], + "c": ["b": (.versionSet(.range("0.0.0" ..< "3.0.0")), .specific(["b"]))], ]) try builder.serve("d", at: "1.0.1") try builder.serve("d", at: "2.3.0") let observability = ObservabilitySystem.makeForTesting() - let resolver = builder.create(pins: [:], delegate: ObservabilityDependencyResolverDelegate(observabilityScope: observability.topScope)) + let resolver = builder.create( + pins: [:], + delegate: ObservabilityDependencyResolverDelegate(observabilityScope: observability.topScope) + ) let dependencies = try builder.create(dependencies: [ - "a": (.versionSet(.range("1.0.0"..<"4.0.0")), .specific(["a"])), - "c": (.versionSet(.range("1.0.0"..<"4.0.0")), .specific(["c"])), - "d": (.versionSet(.range("1.0.0"..<"4.0.0")), .specific(["d"])), + "a": (.versionSet(.range("1.0.0" ..< "4.0.0")), .specific(["a"])), + "c": (.versionSet(.range("1.0.0" ..< "4.0.0")), .specific(["c"])), + "d": (.versionSet(.range("1.0.0" ..< "4.0.0")), .specific(["d"])), ]) let result = resolver.solve(constraints: dependencies) @@ -2923,16 +3255,28 @@ final class PubGrubBacktrackTests: XCTestCase { observability.diagnostics.forEach { print("\($0)") } - XCTAssertTrue(observability.diagnostics.contains(where: { $0.message == "[DependencyResolver] resolved 'a' @ '1.0.0'" })) - XCTAssertTrue(observability.diagnostics.contains(where: { $0.message == "[DependencyResolver] resolved 'b' @ '1.0.1'" })) - XCTAssertTrue(observability.diagnostics.contains(where: { $0.message == "[DependencyResolver] resolved 'c' @ '1.5.2'" })) - XCTAssertTrue(observability.diagnostics.contains(where: { $0.message == "[DependencyResolver] resolved 'd' @ '2.3.0'" })) + XCTAssertTrue( + observability.diagnostics + .contains(where: { $0.message == "[DependencyResolver] resolved 'a' @ '1.0.0'" }) + ) + XCTAssertTrue( + observability.diagnostics + .contains(where: { $0.message == "[DependencyResolver] resolved 'b' @ '1.0.1'" }) + ) + XCTAssertTrue( + observability.diagnostics + .contains(where: { $0.message == "[DependencyResolver] resolved 'c' @ '1.5.2'" }) + ) + XCTAssertTrue( + observability.diagnostics + .contains(where: { $0.message == "[DependencyResolver] resolved 'd' @ '2.3.0'" }) + ) } } -fileprivate extension PinsStore.PinState { +extension PinsStore.PinState { /// Creates a checkout state with the given version and a mocked revision. - static func version(_ version: Version) -> Self { + fileprivate static func version(_ version: Version) -> Self { .version(version, revision: .none) } } @@ -2952,9 +3296,13 @@ private func AssertBindings( pkg.identity != binding.package.identity }) } - .map { $0.package.identity } + .map(\.package.identity) - XCTFail("Unexpected binding(s) found for \(unexpectedBindings.map { $0.description }.joined(separator: ", ")).", file: file, line: line) + XCTFail( + "Unexpected binding(s) found for \(unexpectedBindings.map(\.description).joined(separator: ", ")).", + file: file, + line: line + ) } for package in packages { guard let binding = bindings.first(where: { $0.package.identity == package.identity }) else { @@ -2963,7 +3311,11 @@ private func AssertBindings( } if binding.boundVersion != package.version { - XCTFail("Expected \(package.version) for \(package.identity), found \(binding.boundVersion) instead.", file: file, line: line) + XCTFail( + "Expected \(package.version) for \(package.identity), found \(binding.boundVersion) instead.", + file: file, + line: line + ) } } } @@ -3001,7 +3353,11 @@ private func AssertError( // FIXME: this is not thread-safe public class MockContainer: PackageContainer { - public typealias Dependency = (container: PackageReference, requirement: PackageRequirement, productFilter: ProductFilter) + public typealias Dependency = ( + container: PackageReference, + requirement: PackageRequirement, + productFilter: ProductFilter + ) public var package: PackageReference var manifestName: PackageReference? @@ -3011,7 +3367,7 @@ public class MockContainer: PackageContainer { public var unversionedDeps: [PackageContainerConstraint] = [] /// The list of versions that have incompatible tools version. - var toolsVersion: ToolsVersion = ToolsVersion.current + var toolsVersion: ToolsVersion = .current var versionsToolsVersions = [Version: ToolsVersion]() private var _versions: [BoundVersion] @@ -3026,7 +3382,7 @@ public class MockContainer: PackageContainer { return versions } - public func versionsAscending() throws -> [Version] { + public func versionsAscending() throws -> [Version] { var versions: [Version] = [] for version in self._versions { guard case .version(let v) = version else { continue } @@ -3053,11 +3409,17 @@ public class MockContainer: PackageContainer { return version } - public func getDependencies(at version: Version, productFilter: ProductFilter) throws -> [PackageContainerConstraint] { - return try getDependencies(at: version.description, productFilter: productFilter) + public func getDependencies( + at version: Version, + productFilter: ProductFilter + ) throws -> [PackageContainerConstraint] { + try self.getDependencies(at: version.description, productFilter: productFilter) } - public func getDependencies(at revision: String, productFilter: ProductFilter) throws -> [PackageContainerConstraint] { + public func getDependencies( + at revision: String, + productFilter: ProductFilter + ) throws -> [PackageContainerConstraint] { guard let revisionDependencies = dependencies[revision] else { throw _MockLoadingError.unknownRevision } @@ -3065,18 +3427,18 @@ public class MockContainer: PackageContainer { for (product, productDependencies) in revisionDependencies where productFilter.contains(product) { filteredDependencies.append(contentsOf: productDependencies) } - return filteredDependencies.map({ value in + return filteredDependencies.map { value in let (package, requirement, filter) = value return PackageContainerConstraint(package: package, requirement: requirement, products: filter) - }) + } } public func getUnversionedDependencies(productFilter: ProductFilter) throws -> [PackageContainerConstraint] { // FIXME: This is messy, remove unversionedDeps property. - if !unversionedDeps.isEmpty { - return unversionedDeps + if !self.unversionedDeps.isEmpty { + return self.unversionedDeps } - return try getDependencies(at: PackageRequirement.unversioned.description, productFilter: productFilter) + return try self.getDependencies(at: PackageRequirement.unversioned.description, productFilter: productFilter) } public func loadPackageReference(at boundVersion: BoundVersion) throws -> PackageReference { @@ -3099,11 +3461,19 @@ public class MockContainer: PackageContainer { public convenience init( package: PackageReference, - unversionedDependencies: [(package: PackageReference, requirement: PackageRequirement, productFilter: ProductFilter)] + unversionedDependencies: [( + package: PackageReference, + requirement: PackageRequirement, + productFilter: ProductFilter + )] ) { self.init(package: package) self.unversionedDeps = unversionedDependencies - .map { PackageContainerConstraint(package: $0.package, requirement: $0.requirement, products: $0.productFilter) } + .map { PackageContainerConstraint( + package: $0.package, + requirement: $0.requirement, + products: $0.productFilter + ) } } public convenience init( @@ -3112,16 +3482,17 @@ public class MockContainer: PackageContainer { package: PackageReference, requirement: VersionSetSpecifier, productFilter: ProductFilter - )]]]) { + )]]] + ) { var dependencies: [String: [String: [Dependency]]] = [:] for (version, productDependencies) in dependenciesByVersion { if dependencies[version.description] == nil { dependencies[version.description] = [:] } for (product, deps) in productDependencies { - dependencies[version.description, default: [:]][product] = deps.map({ + dependencies[version.description, default: [:]][product] = deps.map { ($0.package, .versionSet($0.requirement), $0.productFilter) - }) + } } } self.init(package: package, dependencies: dependencies) @@ -3146,13 +3517,12 @@ public enum _MockLoadingError: Error { } public struct MockProvider: PackageContainerProvider { - public let containers: [MockContainer] public let containersByIdentifier: [PackageReference: MockContainer] public init(containers: [MockContainer]) { self.containers = containers - self.containersByIdentifier = Dictionary(uniqueKeysWithValues: containers.map({ ($0.package, $0) })) + self.containersByIdentifier = Dictionary(uniqueKeysWithValues: containers.map { ($0.package, $0) }) } public func getContainer( @@ -3160,11 +3530,15 @@ public struct MockProvider: PackageContainerProvider { updateStrategy: ContainerUpdateStrategy, observabilityScope: ObservabilityScope, on queue: DispatchQueue, - completion: @escaping (Result - ) -> Void) { + completion: @escaping ( + Result + ) -> Void + ) { queue.async { - completion(self.containersByIdentifier[package].map{ .success($0) } ?? - .failure(_MockLoadingError.unknownModule)) + completion( + self.containersByIdentifier[package].map { .success($0) } ?? + .failure(_MockLoadingError.unknownModule) + ) } } } @@ -3177,7 +3551,10 @@ class DependencyGraphBuilder { if let reference = self.references[packageName] { return reference } - let newReference = PackageReference.localSourceControl(identity: .plain(packageName), path: try .init(validating: "/\(packageName)")) + let newReference = try PackageReference.localSourceControl( + identity: .plain(packageName), + path: .init(validating: "/\(packageName)") + ) self.references[packageName] = newReference return newReference } @@ -3185,9 +3562,12 @@ class DependencyGraphBuilder { func create( dependencies: OrderedCollections.OrderedDictionary ) throws -> [PackageContainerConstraint] { - var refDependencies = OrderedCollections.OrderedDictionary() + var refDependencies = OrderedCollections.OrderedDictionary< + PackageReference, + (PackageRequirement, ProductFilter) + >() for dependency in dependencies { - try refDependencies[reference(for: dependency.key)] = dependency.value + try refDependencies[self.reference(for: dependency.key)] = dependency.value } return self.create(dependencies: refDependencies) } @@ -3195,7 +3575,7 @@ class DependencyGraphBuilder { func create( dependencies: OrderedCollections.OrderedDictionary ) -> [PackageContainerConstraint] { - return dependencies.map { + dependencies.map { PackageContainerConstraint(package: $0, requirement: $1.0, products: $1.1) } } @@ -3204,16 +3584,22 @@ class DependencyGraphBuilder { _ package: String, at versions: [Version], toolsVersion: ToolsVersion? = nil, - with dependencies: KeyValuePairs> = [:] + with dependencies: KeyValuePairs< + String, + OrderedCollections.OrderedDictionary + > = [:] ) throws { - try self.serve(package, at: versions.map{ .version($0) }, toolsVersion: toolsVersion, with: dependencies) + try self.serve(package, at: versions.map { .version($0) }, toolsVersion: toolsVersion, with: dependencies) } func serve( _ package: String, at version: Version, toolsVersion: ToolsVersion? = nil, - with dependencies: KeyValuePairs> = [:] + with dependencies: KeyValuePairs< + String, + OrderedCollections.OrderedDictionary + > = [:] ) throws { try self.serve(package, at: .version(version), toolsVersion: toolsVersion, with: dependencies) } @@ -3222,7 +3608,10 @@ class DependencyGraphBuilder { _ package: String, at versions: [BoundVersion], toolsVersion: ToolsVersion? = nil, - with dependencies: KeyValuePairs> = [:] + with dependencies: KeyValuePairs< + String, + OrderedCollections.OrderedDictionary + > = [:] ) throws { let packageReference = try reference(for: package) try self.serve( @@ -3237,7 +3626,10 @@ class DependencyGraphBuilder { _ package: String, at version: BoundVersion, toolsVersion: ToolsVersion? = nil, - with dependencies: KeyValuePairs> = [:] + with dependencies: KeyValuePairs< + String, + OrderedCollections.OrderedDictionary + > = [:] ) throws { let packageReference = try reference(for: package) try self.serve( @@ -3252,10 +3644,13 @@ class DependencyGraphBuilder { _ packageReference: PackageReference, at versions: [BoundVersion], toolsVersion: ToolsVersion? = nil, - with dependencies: KeyValuePairs> = [:] + with dependencies: KeyValuePairs< + String, + OrderedCollections.OrderedDictionary + > = [:] ) throws { for version in versions { - try serve(packageReference, at: version, toolsVersion: toolsVersion, with: dependencies) + try self.serve(packageReference, at: version, toolsVersion: toolsVersion, with: dependencies) } } @@ -3263,9 +3658,13 @@ class DependencyGraphBuilder { _ packageReference: PackageReference, at version: BoundVersion, toolsVersion: ToolsVersion? = nil, - with dependencies: KeyValuePairs> = [:] + with dependencies: KeyValuePairs< + String, + OrderedCollections.OrderedDictionary + > = [:] ) throws { - let container = self.containers[packageReference.identity.description] ?? MockContainer(package: packageReference) + let container = self + .containers[packageReference.identity.description] ?? MockContainer(package: packageReference) if case .version(let v) = version { container.versionsToolsVersions[v] = toolsVersion ?? container.toolsVersion @@ -3277,8 +3676,8 @@ class DependencyGraphBuilder { container.dependencies[version.description] = [:] } for (product, filteredDependencies) in dependencies { - let packageDependencies: [MockContainer.Dependency] = try filteredDependencies.map { - (container: try reference(for: $0), requirement: $1.0, productFilter: $1.1) + let packageDependencies: [MockContainer.Dependency] = filteredDependencies.map { + (container: $0, requirement: $1.0, productFilter: $1.1) } container.dependencies[version.description, default: [:]][product, default: []] += packageDependencies } @@ -3288,24 +3687,38 @@ class DependencyGraphBuilder { /// Creates a pins store with the given pins. func create(pinsStore pins: [String: (PinsStore.PinState, ProductFilter)]) throws -> PinsStore { let fs = InMemoryFileSystem() - let store = try! PinsStore(pinsFile: "/tmp/Package.resolved", workingDirectory: .root, fileSystem: fs, mirrors: .init()) + let store = try! PinsStore( + pinsFile: "/tmp/Package.resolved", + workingDirectory: .root, + fileSystem: fs, + mirrors: .init() + ) for (package, pin) in pins { - store.pin(packageRef: try reference(for: package), state: pin.0) + try store.pin(packageRef: self.reference(for: package), state: pin.0) } try! store.saveState(toolsVersion: ToolsVersion.current, originHash: .none) return store } - - func create(pins: PinsStore.Pins = [:], delegate: DependencyResolverDelegate? = .none) -> PubGrubDependencyResolver { + func create( + pins: PinsStore.Pins = [:], + availableLibraries: [LibraryMetadata] = [], + delegate: DependencyResolverDelegate? = .none + ) -> PubGrubDependencyResolver { defer { self.containers = [:] self.references = [:] } let provider = MockProvider(containers: Array(self.containers.values)) - return PubGrubDependencyResolver(provider :provider, pins: pins, observabilityScope: ObservabilitySystem.NOOP, delegate: delegate) + return PubGrubDependencyResolver( + provider: provider, + pins: pins, + availableLibraries: availableLibraries, + observabilityScope: ObservabilitySystem.NOOP, + delegate: delegate + ) } } @@ -3334,26 +3747,35 @@ extension Term { } else if value.contains("^") { components = value.split(separator: "^").map(String.init) let upperMajor = Int(String(components[1].split(separator: ".").first!))! + 1 - requirement = .versionSet(.range(Version(stringLiteral: components[1]).. Date: Tue, 9 Apr 2024 18:03:48 +0100 Subject: [PATCH 072/159] Fix sendability warnings in `URLSessionHTTPClient` (#7441) Achieved by converting `DownloadTask` and `DataTask` to value types and adding more `Sendable` annotations where needed. --- Sources/Basics/AuthorizationProvider.swift | 7 ++--- .../Basics/HTTPClient/LegacyHTTPClient.swift | 18 +++++------ .../HTTPClient/URLSessionHTTPClient.swift | 31 ++++++++++++------- Sources/Build/BuildOperation.swift | 2 -- Sources/PackageRegistry/RegistryClient.swift | 2 +- 5 files changed, 32 insertions(+), 28 deletions(-) diff --git a/Sources/Basics/AuthorizationProvider.swift b/Sources/Basics/AuthorizationProvider.swift index 11e80cb79ed..89326664a3c 100644 --- a/Sources/Basics/AuthorizationProvider.swift +++ b/Sources/Basics/AuthorizationProvider.swift @@ -17,8 +17,7 @@ import struct Foundation.URL import Security #endif -public protocol AuthorizationProvider { - @Sendable +public protocol AuthorizationProvider: Sendable { func authentication(for url: URL) -> (user: String, password: String)? } @@ -80,7 +79,7 @@ extension AuthorizationProvider { // MARK: - netrc -public class NetrcAuthorizationProvider: AuthorizationProvider, AuthorizationWriter { +public final class NetrcAuthorizationProvider: AuthorizationProvider, AuthorizationWriter { // marked internal for testing internal let path: AbsolutePath private let fileSystem: FileSystem @@ -202,7 +201,7 @@ public class NetrcAuthorizationProvider: AuthorizationProvider, AuthorizationWri // MARK: - Keychain #if canImport(Security) -public class KeychainAuthorizationProvider: AuthorizationProvider, AuthorizationWriter { +public final class KeychainAuthorizationProvider: AuthorizationProvider, AuthorizationWriter { private let observabilityScope: ObservabilityScope private let cache = ThreadSafeKeyValueStore() diff --git a/Sources/Basics/HTTPClient/LegacyHTTPClient.swift b/Sources/Basics/HTTPClient/LegacyHTTPClient.swift index 8b53d663b4d..b5294ccfabf 100644 --- a/Sources/Basics/HTTPClient/LegacyHTTPClient.swift +++ b/Sources/Basics/HTTPClient/LegacyHTTPClient.swift @@ -26,8 +26,8 @@ public final class LegacyHTTPClient: Cancellable { public typealias Request = LegacyHTTPClientRequest public typealias Response = HTTPClientResponse public typealias Handler = (Request, ProgressHandler?, @escaping (Result) -> Void) -> Void - public typealias ProgressHandler = (_ bytesReceived: Int64, _ totalBytes: Int64?) throws -> Void - public typealias CompletionHandler = (Result) -> Void + public typealias ProgressHandler = @Sendable (_ bytesReceived: Int64, _ totalBytes: Int64?) throws -> Void + public typealias CompletionHandler = @Sendable (Result) -> Void public var configuration: LegacyHTTPClientConfiguration private let underlying: Handler @@ -121,7 +121,7 @@ public final class LegacyHTTPClient: Cancellable { requestNumber: 0, observabilityScope: observabilityScope, progress: progress.map { handler in - { received, expected in + { @Sendable received, expected in // call back on the requested queue callbackQueue.async { do { @@ -312,7 +312,7 @@ extension LegacyHTTPClient { headers: HTTPClientHeaders = .init(), options: Request.Options = .init(), observabilityScope: ObservabilityScope? = .none, - completion: @escaping (Result) -> Void + completion: @Sendable @escaping (Result) -> Void ) { self.execute( Request(method: .head, url: url, headers: headers, body: nil, options: options), @@ -326,7 +326,7 @@ extension LegacyHTTPClient { headers: HTTPClientHeaders = .init(), options: Request.Options = .init(), observabilityScope: ObservabilityScope? = .none, - completion: @escaping (Result) -> Void + completion: @Sendable @escaping (Result) -> Void ) { self.execute( Request(method: .get, url: url, headers: headers, body: nil, options: options), @@ -341,7 +341,7 @@ extension LegacyHTTPClient { headers: HTTPClientHeaders = .init(), options: Request.Options = .init(), observabilityScope: ObservabilityScope? = .none, - completion: @escaping (Result) -> Void + completion: @Sendable @escaping (Result) -> Void ) { self.execute( Request(method: .put, url: url, headers: headers, body: body, options: options), @@ -356,7 +356,7 @@ extension LegacyHTTPClient { headers: HTTPClientHeaders = .init(), options: Request.Options = .init(), observabilityScope: ObservabilityScope? = .none, - completion: @escaping (Result) -> Void + completion: @Sendable @escaping (Result) -> Void ) { self.execute( Request(method: .post, url: url, headers: headers, body: body, options: options), @@ -370,7 +370,7 @@ extension LegacyHTTPClient { headers: HTTPClientHeaders = .init(), options: Request.Options = .init(), observabilityScope: ObservabilityScope? = .none, - completion: @escaping (Result) -> Void + completion: @Sendable @escaping (Result) -> Void ) { self.execute( Request(method: .delete, url: url, headers: headers, body: nil, options: options), @@ -383,7 +383,7 @@ extension LegacyHTTPClient { // MARK: - LegacyHTTPClientConfiguration public struct LegacyHTTPClientConfiguration { - public typealias AuthorizationProvider = (URL) -> String? + public typealias AuthorizationProvider = @Sendable (URL) -> String? public var requestHeaders: HTTPClientHeaders? public var requestTimeout: DispatchTimeInterval? diff --git a/Sources/Basics/HTTPClient/URLSessionHTTPClient.swift b/Sources/Basics/HTTPClient/URLSessionHTTPClient.swift index a3d4102da64..b706b173d39 100644 --- a/Sources/Basics/HTTPClient/URLSessionHTTPClient.swift +++ b/Sources/Basics/HTTPClient/URLSessionHTTPClient.swift @@ -19,7 +19,7 @@ import struct TSCUtility.Versioning import FoundationNetworking #endif -final class URLSessionHTTPClient { +final class URLSessionHTTPClient: Sendable { private let dataTaskManager: DataTaskManager private let downloadTaskManager: DownloadTaskManager @@ -42,7 +42,7 @@ final class URLSessionHTTPClient { urlRequest: urlRequest, authorizationProvider: request.options.authorizationProvider, progress: progress, - completion: continuation.resume(with:) + completion: { continuation.resume(with: $0) } ) case .download(_, let destination): task = self.downloadTaskManager.makeTask( @@ -52,13 +52,14 @@ final class URLSessionHTTPClient { fileSystem: localFileSystem, destination: destination, progress: progress, - completion: continuation.resume(with:) + completion: { continuation.resume(with: $0) } ) } task.resume() } } + @Sendable public func execute( _ request: LegacyHTTPClient.Request, progress: LegacyHTTPClient.ProgressHandler?, @@ -140,8 +141,8 @@ private class WeakDataTaskManager: NSObject, URLSessionDataDelegate { } } -private class DataTaskManager { - private var tasks = ThreadSafeKeyValueStore() +private final class DataTaskManager: @unchecked Sendable { + private let tasks = ThreadSafeKeyValueStore() private let delegateQueue: OperationQueue private var session: URLSession! @@ -179,11 +180,13 @@ private class DataTaskManager { didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void ) { - guard let task = self.tasks[dataTask.taskIdentifier] else { + guard var task = self.tasks[dataTask.taskIdentifier] else { return completionHandler(.cancel) } task.response = response as? HTTPURLResponse task.expectedContentLength = response.expectedContentLength + self.tasks[dataTask.taskIdentifier] = task + do { try task.progressHandler?(0, response.expectedContentLength) completionHandler(.allow) @@ -193,7 +196,7 @@ private class DataTaskManager { } public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { - guard let task = self.tasks[dataTask.taskIdentifier] else { + guard var task = self.tasks[dataTask.taskIdentifier] else { return } if task.buffer != nil { @@ -201,6 +204,7 @@ private class DataTaskManager { } else { task.buffer = data } + self.tasks[dataTask.taskIdentifier] = task do { // safe since created in the line above @@ -246,7 +250,7 @@ private class DataTaskManager { completionHandler(request) } - class DataTask { + struct DataTask: Sendable { let task: URLSessionDataTask let completionHandler: LegacyHTTPClient.CompletionHandler /// A strong reference to keep the `DataTaskManager` alive so it can handle the callbacks from the @@ -318,9 +322,11 @@ private class WeakDownloadTaskManager: NSObject, URLSessionDownloadDelegate { } } -private class DownloadTaskManager { - private var tasks = ThreadSafeKeyValueStore() +private final class DownloadTaskManager: @unchecked Sendable { + private let tasks = ThreadSafeKeyValueStore() private let delegateQueue: OperationQueue + + // FIXME: can't be `let` instead of `var`, as `URLSession` holds a reference to `self`. private var session: URLSession! init(configuration: URLSessionConfiguration) { @@ -379,7 +385,7 @@ private class DownloadTaskManager { downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL ) { - guard let task = self.tasks[downloadTask.taskIdentifier] else { + guard var task = self.tasks[downloadTask.taskIdentifier] else { return } @@ -392,6 +398,7 @@ private class DownloadTaskManager { try task.fileSystem.move(from: path, to: task.destination) } catch { task.moveFileError = error + self.tasks[downloadTask.taskIdentifier] = task } } @@ -419,7 +426,7 @@ private class DownloadTaskManager { } } - class DownloadTask { + struct DownloadTask: Sendable { let task: URLSessionDownloadTask let fileSystem: FileSystem let destination: AbsolutePath diff --git a/Sources/Build/BuildOperation.swift b/Sources/Build/BuildOperation.swift index 48ab75d839c..847f941bc46 100644 --- a/Sources/Build/BuildOperation.swift +++ b/Sources/Build/BuildOperation.swift @@ -12,8 +12,6 @@ import Basics -import Build - import LLBuildManifest import PackageGraph import PackageLoading diff --git a/Sources/PackageRegistry/RegistryClient.swift b/Sources/PackageRegistry/RegistryClient.swift index ff8ec74b5c6..a13fc4cd8f6 100644 --- a/Sources/PackageRegistry/RegistryClient.swift +++ b/Sources/PackageRegistry/RegistryClient.swift @@ -1651,7 +1651,7 @@ public final class RegistryClient: Cancellable { result.tryMap { response in observabilityScope .emit( - debug: "server response for \(request.url): \(response.statusCode) in \(start.distance(to: .now()).descriptionInSeconds)" + debug: "server response for \(url): \(response.statusCode) in \(start.distance(to: .now()).descriptionInSeconds)" ) switch response.statusCode { case 201: From 72b84988ae668aa0d3d5ec24b5f7f0c99c9884bb Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 9 Apr 2024 21:12:57 +0100 Subject: [PATCH 073/159] NFC: Fix `@Sendable @escaping` order, symbol name typo (#7446) Sticking to `@escaping @Sendable` as a preferred order of attributes. Also fixed `dependecies` -> `dependencies` typo. --- .../Basics/Concurrency/ConcurrencyHelpers.swift | 2 +- Sources/Basics/FileSystem/TemporaryFile.swift | 2 +- Sources/Basics/Observability.swift | 3 +-- Sources/CoreCommands/BuildSystemSupport.swift | 2 +- Sources/CoreCommands/SwiftCommandState.swift | 2 +- Sources/PackageRegistry/SignatureValidation.swift | 14 +++++++------- 6 files changed, 12 insertions(+), 13 deletions(-) diff --git a/Sources/Basics/Concurrency/ConcurrencyHelpers.swift b/Sources/Basics/Concurrency/ConcurrencyHelpers.swift index 24330b3e79f..3a10d61a81c 100644 --- a/Sources/Basics/Concurrency/ConcurrencyHelpers.swift +++ b/Sources/Basics/Concurrency/ConcurrencyHelpers.swift @@ -49,7 +49,7 @@ extension DispatchQueue { /// Bridges between potentially blocking methods that take a result completion closure and async/await public func safe_async( - _ body: @Sendable @escaping (@Sendable @escaping (Result) -> Void) -> Void + _ body: @escaping @Sendable (@escaping @Sendable (Result) -> Void) -> Void ) async throws -> T { try await withCheckedThrowingContinuation { continuation in // It is possible that body make block indefinitely on a lock, semaphore, diff --git a/Sources/Basics/FileSystem/TemporaryFile.swift b/Sources/Basics/FileSystem/TemporaryFile.swift index a8e31126448..756f5022f4a 100644 --- a/Sources/Basics/FileSystem/TemporaryFile.swift +++ b/Sources/Basics/FileSystem/TemporaryFile.swift @@ -34,7 +34,7 @@ public func withTemporaryDirectory( fileSystem: FileSystem = localFileSystem, dir: AbsolutePath? = nil, prefix: String = "TemporaryDirectory", - _ body: @Sendable @escaping (AbsolutePath, @escaping (AbsolutePath) -> Void) async throws -> Result + _ body: @escaping @Sendable (AbsolutePath, @escaping (AbsolutePath) -> Void) async throws -> Result ) throws -> Task { let temporaryDirectory = try createTemporaryDirectory(fileSystem: fileSystem, dir: dir, prefix: prefix) diff --git a/Sources/Basics/Observability.swift b/Sources/Basics/Observability.swift index 4d6e01f959b..2567c6be7ec 100644 --- a/Sources/Basics/Observability.swift +++ b/Sources/Basics/Observability.swift @@ -46,8 +46,7 @@ public class ObservabilitySystem { private struct SingleDiagnosticsHandler: ObservabilityHandlerProvider, DiagnosticsHandler { var diagnosticsHandler: DiagnosticsHandler { self } - let underlying: @Sendable (ObservabilityScope, Diagnostic) - -> Void + let underlying: @Sendable (ObservabilityScope, Diagnostic) -> Void init(_ underlying: @escaping @Sendable (ObservabilityScope, Diagnostic) -> Void) { self.underlying = underlying diff --git a/Sources/CoreCommands/BuildSystemSupport.swift b/Sources/CoreCommands/BuildSystemSupport.swift index da932c387af..c2e7bd0c4c2 100644 --- a/Sources/CoreCommands/BuildSystemSupport.swift +++ b/Sources/CoreCommands/BuildSystemSupport.swift @@ -53,7 +53,7 @@ private struct NativeBuildSystemFactory: BuildSystemFactory { ), additionalFileRules: FileRuleDescription.swiftpmFileTypes, pkgConfigDirectories: self.swiftCommandState.options.locations.pkgConfigDirectories, - dependenciesByRootPackageIdentity: rootPackageInfo.dependecies, + dependenciesByRootPackageIdentity: rootPackageInfo.dependencies, targetsByRootPackageIdentity: rootPackageInfo.targets, outputStream: outputStream ?? self.swiftCommandState.outputStream, logLevel: logLevel ?? self.swiftCommandState.logLevel, diff --git a/Sources/CoreCommands/SwiftCommandState.swift b/Sources/CoreCommands/SwiftCommandState.swift index 38c190ec386..754a5401ea4 100644 --- a/Sources/CoreCommands/SwiftCommandState.swift +++ b/Sources/CoreCommands/SwiftCommandState.swift @@ -463,7 +463,7 @@ package final class SwiftCommandState { return workspace } - package func getRootPackageInformation() throws -> (dependecies: [PackageIdentity: [PackageIdentity]], targets: [PackageIdentity: [String]]) { + package func getRootPackageInformation() throws -> (dependencies: [PackageIdentity: [PackageIdentity]], targets: [PackageIdentity: [String]]) { let workspace = try self.getActiveWorkspace() let root = try self.getWorkspaceRoot() let rootManifests = try temp_await { diff --git a/Sources/PackageRegistry/SignatureValidation.swift b/Sources/PackageRegistry/SignatureValidation.swift index 3e5fccd64ae..4fee446f87f 100644 --- a/Sources/PackageRegistry/SignatureValidation.swift +++ b/Sources/PackageRegistry/SignatureValidation.swift @@ -91,7 +91,7 @@ struct SignatureValidation { fileSystem: FileSystem, observabilityScope: ObservabilityScope, callbackQueue: DispatchQueue, - completion: @Sendable @escaping (Result) -> Void + completion: @escaping @Sendable (Result) -> Void ) { guard !self.skipSignatureValidation else { return completion(.success(.none)) @@ -138,7 +138,7 @@ struct SignatureValidation { fileSystem: FileSystem, observabilityScope: ObservabilityScope, callbackQueue: DispatchQueue, - completion: @Sendable @escaping (Result) -> Void + completion: @escaping @Sendable (Result) -> Void ) { do { let versionMetadata = try self.versionMetadataProvider(package, version) @@ -240,7 +240,7 @@ struct SignatureValidation { configuration: RegistryConfiguration.Security.Signing, fileSystem: FileSystem, observabilityScope: ObservabilityScope, - completion: @Sendable @escaping (Result) -> Void + completion: @escaping @Sendable (Result) -> Void ) { Task { do { @@ -353,7 +353,7 @@ struct SignatureValidation { fileSystem: FileSystem, observabilityScope: ObservabilityScope, callbackQueue: DispatchQueue, - completion: @Sendable @escaping (Result) -> Void + completion: @escaping @Sendable (Result) -> Void ) { guard !self.skipSignatureValidation else { return completion(.success(.none)) @@ -402,7 +402,7 @@ struct SignatureValidation { fileSystem: FileSystem, observabilityScope: ObservabilityScope, callbackQueue: DispatchQueue, - completion: @Sendable @escaping (Result) -> Void + completion: @escaping @Sendable (Result) -> Void ) { let manifestName = toolsVersion.map { "Package@swift-\($0).swift" } ?? Manifest.filename @@ -506,7 +506,7 @@ struct SignatureValidation { configuration: RegistryConfiguration.Security.Signing, fileSystem: FileSystem, observabilityScope: ObservabilityScope, - completion: @Sendable @escaping (Result) -> Void + completion: @escaping @Sendable (Result) -> Void ) { Task { do { @@ -577,7 +577,7 @@ struct SignatureValidation { signatureFormat: SignatureFormat, configuration: RegistryConfiguration.Security.Signing, fileSystem: FileSystem, - completion: @Sendable @escaping (Result) -> Void + completion: @escaping @Sendable (Result) -> Void ) { Task { do { From 017192c2bf1be840a1585cafae7f6b74d9db7000 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Wed, 10 Apr 2024 00:49:11 +0100 Subject: [PATCH 074/159] Bump `swift-syntax` in template to `600.0.0-latest` (#7443) rdar://124160537 --- .../PackageModel/InstalledSwiftPMConfiguration.swift | 10 +++++++++- Utilities/config.json | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Sources/PackageModel/InstalledSwiftPMConfiguration.swift b/Sources/PackageModel/InstalledSwiftPMConfiguration.swift index e89788cb2ad..a34a3d22dad 100644 --- a/Sources/PackageModel/InstalledSwiftPMConfiguration.swift +++ b/Sources/PackageModel/InstalledSwiftPMConfiguration.swift @@ -33,6 +33,14 @@ public struct InstalledSwiftPMConfiguration: Codable { public let swiftSyntaxVersionForMacroTemplate: Version public static var `default`: InstalledSwiftPMConfiguration { - return .init(version: 0, swiftSyntaxVersionForMacroTemplate: .init(major: 509, minor: 0, patch: 0)) + return .init( + version: 0, + swiftSyntaxVersionForMacroTemplate: .init( + major: 600, + minor: 0, + patch: 0, + prereleaseIdentifier: "latest" + ) + ) } } diff --git a/Utilities/config.json b/Utilities/config.json index 5e693bf466e..dc8ec6cafe9 100644 --- a/Utilities/config.json +++ b/Utilities/config.json @@ -1 +1 @@ -{"version":1,"swiftSyntaxVersionForMacroTemplate":{"major":510,"minor":0,"patch":0, "prereleaseIdentifier":"latest"}} \ No newline at end of file +{"version":1,"swiftSyntaxVersionForMacroTemplate":{"major":600,"minor":0,"patch":0, "prereleaseIdentifier":"latest"}} From 382fc2e793eb00010fb245cb61133054a43cf6e4 Mon Sep 17 00:00:00 2001 From: Jake Petroules Date: Wed, 10 Apr 2024 00:05:19 -0700 Subject: [PATCH 075/159] Update XCBuildSupport CODEOWNERS Standalone files in subdirectories don't work. --- CODEOWNERS | 2 ++ Sources/XCBuildSupport/CODEOWNERS | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) delete mode 100644 Sources/XCBuildSupport/CODEOWNERS diff --git a/CODEOWNERS b/CODEOWNERS index 3d5fc3efd50..b3e12d62317 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -24,4 +24,6 @@ # The following lines are used by GitHub to automatically recommend reviewers. +Sources/XCBuildSupport/* @jakepetroules + * @bnbarham @MaxDesiatov @tomerd diff --git a/Sources/XCBuildSupport/CODEOWNERS b/Sources/XCBuildSupport/CODEOWNERS deleted file mode 100644 index cb9a8e18841..00000000000 --- a/Sources/XCBuildSupport/CODEOWNERS +++ /dev/null @@ -1 +0,0 @@ -@jakepetroules From 3dc16dbc54f23faace996054cd23d1976faf0ec4 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Wed, 10 Apr 2024 18:40:21 +0100 Subject: [PATCH 076/159] Add basic `QueryEngine` scaffolding w/ `Encodable` hashing (#7347) ### Motivation: Caching of computations in SwiftPM is ad-hoc and mostly relies on in-memory `ThreadSafeKeyValueStore`, which utilizes locking and makes it harder to add `Sendable` conformances on types that have these stores as properties. To allow consistent and persistent caching when SwiftPM processes are relaunched, we can use a SQLite-backed async-first caching engine. ### Modifications: This change ports [most of the current `GeneratorEngine` implementation](https://github.com/apple/swift-sdk-generator/blob/main/Sources/GeneratorEngine/Engine.swift) from the Swift SDK Generator repository to the SwiftPM code base as `QueryEngine`. Since SwiftNIO is not supported on Windows, references to `AsyncHTTPClient` have been removed. Additionally, we can't use macros in the SwiftPM code base either, thus the `Query` protocol has to conform to `Encodable` instead of using macro-generated conformances. We don't have a consistent hashing implementation for `Encodable` yet, and a temporary stub for it is marked with `fatalError` for now. ### Result: NFC, new `QueryEngine` module is not used anywhere yet. --- Package.swift | 78 +++++++----- .../Basics/Concurrency/ThrowingDefer.swift | 59 +++++++++ Sources/Basics/SQLiteBackedCache.swift | 45 ++++++- .../FileSystem/AsyncFileSystem.swift | 37 ++++++ .../FileSystem/FileCacheRecord.swift | 39 ++++++ .../FileSystem/OpenReadableFile.swift | 54 ++++++++ .../FileSystem/OpenWritableFile.swift | 32 +++++ .../FileSystem/ReadableFileStream.swift | 104 ++++++++++++++++ .../FileSystem/VirtualFileSystem.swift | 45 +++++++ Sources/QueryEngine/Query.swift | 23 ++++ Sources/QueryEngine/QueryEngine.swift | 116 ++++++++++++++++++ 11 files changed, 596 insertions(+), 36 deletions(-) create mode 100644 Sources/Basics/Concurrency/ThrowingDefer.swift create mode 100644 Sources/QueryEngine/FileSystem/AsyncFileSystem.swift create mode 100644 Sources/QueryEngine/FileSystem/FileCacheRecord.swift create mode 100644 Sources/QueryEngine/FileSystem/OpenReadableFile.swift create mode 100644 Sources/QueryEngine/FileSystem/OpenWritableFile.swift create mode 100644 Sources/QueryEngine/FileSystem/ReadableFileStream.swift create mode 100644 Sources/QueryEngine/FileSystem/VirtualFileSystem.swift create mode 100644 Sources/QueryEngine/Query.swift create mode 100644 Sources/QueryEngine/QueryEngine.swift diff --git a/Package.swift b/Package.swift index 5ca57fece39..b8f4923719d 100644 --- a/Package.swift +++ b/Package.swift @@ -12,26 +12,34 @@ // //===----------------------------------------------------------------------===// -import PackageDescription import class Foundation.ProcessInfo +import PackageDescription // When building the toolchain on the CI for ELF platforms, remove the CI's // stdlib absolute runpath and add ELF's $ORIGIN relative paths before installing. -let swiftpmLinkSettings : [LinkerSetting] -let packageLibraryLinkSettings : [LinkerSetting] +let swiftpmLinkSettings: [LinkerSetting] +let packageLibraryLinkSettings: [LinkerSetting] if let resourceDirPath = ProcessInfo.processInfo.environment["SWIFTCI_INSTALL_RPATH_OS"] { - swiftpmLinkSettings = [ .unsafeFlags(["-no-toolchain-stdlib-rpath", "-Xlinker", "-rpath", "-Xlinker", "$ORIGIN/../lib/swift/\(resourceDirPath)"]) ] - packageLibraryLinkSettings = [ .unsafeFlags(["-no-toolchain-stdlib-rpath", "-Xlinker", "-rpath", "-Xlinker", "$ORIGIN/../../\(resourceDirPath)"]) ] + swiftpmLinkSettings = [.unsafeFlags([ + "-no-toolchain-stdlib-rpath", + "-Xlinker", "-rpath", + "-Xlinker", "$ORIGIN/../lib/swift/\(resourceDirPath)", + ])] + packageLibraryLinkSettings = [.unsafeFlags([ + "-no-toolchain-stdlib-rpath", + "-Xlinker", "-rpath", + "-Xlinker", "$ORIGIN/../../\(resourceDirPath)", + ])] } else { - swiftpmLinkSettings = [] - packageLibraryLinkSettings = [] + swiftpmLinkSettings = [] + packageLibraryLinkSettings = [] } /** SwiftPMDataModel is the subset of SwiftPM product that includes just its data model. -This allows some clients (such as IDEs) that use SwiftPM's data model but not its build system -to not have to depend on SwiftDriver, SwiftLLBuild, etc. We should probably have better names here, -though that could break some clients. -*/ + This allows some clients (such as IDEs) that use SwiftPM's data model but not its build system + to not have to depend on SwiftDriver, SwiftLLBuild, etc. We should probably have better names here, + though that could break some clients. + */ let swiftPMDataModelProduct = ( name: "SwiftPMDataModel", targets: [ @@ -51,7 +59,7 @@ let swiftPMDataModelProduct = ( command line tools, while `libSwiftPMDataModel` includes only the data model. NOTE: This API is *unstable* and may change at any time. -*/ + */ let swiftPMProduct = ( name: "SwiftPM", targets: swiftPMDataModelProduct.targets + [ @@ -69,8 +77,8 @@ let systemSQLitePkgConfig: String? = "sqlite3" #endif /** An array of products which have two versions listed: one dynamically linked, the other with the -automatic linking type with `-auto` suffix appended to product's name. -*/ + automatic linking type with `-auto` suffix appended to product's name. + */ let autoProducts = [swiftPMProduct, swiftPMDataModelProduct] @@ -90,11 +98,11 @@ let package = Package( name: "SwiftPM", platforms: [ .macOS(.v13), - .iOS(.v16) + .iOS(.v16), ], products: - autoProducts.flatMap { - [ + autoProducts.flatMap { + [ .library( name: $0.name, type: .dynamic, @@ -103,9 +111,9 @@ let package = Package( .library( name: "\($0.name)-auto", targets: $0.targets - ) - ] - } + [ + ), + ] + } + [ .library( name: "XCBuildSupport", targets: ["XCBuildSupport"] @@ -166,7 +174,7 @@ let package = Package( name: "SourceKitLSPAPI", dependencies: [ "Build", - "SPMBuildCore" + "SPMBuildCore", ], exclude: ["CMakeLists.txt"], swiftSettings: [.enableExperimentalFeature("AccessLevelOnImport")] @@ -213,7 +221,7 @@ let package = Package( name: "SourceControl", dependencies: [ "Basics", - "PackageModel" + "PackageModel", ], exclude: ["CMakeLists.txt"] ), @@ -255,7 +263,7 @@ let package = Package( dependencies: [ "Basics", "PackageLoading", - "PackageModel" + "PackageModel", ], exclude: ["CMakeLists.txt", "README.md"] ), @@ -267,7 +275,7 @@ let package = Package( name: "PackageCollectionsModel", dependencies: [], exclude: [ - "Formats/v1.md" + "Formats/v1.md", ] ), @@ -301,7 +309,7 @@ let package = Package( ], exclude: ["CMakeLists.txt"] ), - + .target( name: "PackageSigning", dependencies: [ @@ -320,7 +328,7 @@ let package = Package( name: "SPMBuildCore", dependencies: [ "Basics", - "PackageGraph" + "PackageGraph", ], exclude: ["CMakeLists.txt"] ), @@ -460,6 +468,14 @@ let package = Package( ] ), + .target( + name: "QueryEngine", + dependencies: [ + "Basics", + .product(name: "Crypto", package: "swift-crypto"), + ] + ), + .executableTarget( /** The main executable provided by SwiftPM */ name: "swift-package", @@ -562,7 +578,8 @@ let package = Package( .target( /** Test for thread-santizer. */ name: "tsan_utils", - dependencies: []), + dependencies: [] + ), // MARK: SwiftPM tests @@ -664,7 +681,7 @@ let package = Package( name: "XCBuildSupportTests", dependencies: ["XCBuildSupport", "SPMTestSupport"], exclude: ["Inputs/Foo.pc"] - ) + ), ], swiftLanguageVersions: [.v5] ) @@ -682,7 +699,8 @@ package.targets.append(contentsOf: [ ), ]) -// rdar://101868275 "error: cannot find 'XCTAssertEqual' in scope" can affect almost any functional test, so we flat out disable them all until we know what is going on +// rdar://101868275 "error: cannot find 'XCTAssertEqual' in scope" can affect almost any functional test, so we flat out +// disable them all until we know what is going on if ProcessInfo.processInfo.environment["SWIFTCI_DISABLE_SDK_DEPENDENT_TESTS"] == nil { package.targets.append(contentsOf: [ .testTarget( @@ -690,7 +708,7 @@ if ProcessInfo.processInfo.environment["SWIFTCI_DISABLE_SDK_DEPENDENT_TESTS"] == dependencies: [ "swift-package-manager", "PackageModel", - "SPMTestSupport" + "SPMTestSupport", ] ), diff --git a/Sources/Basics/Concurrency/ThrowingDefer.swift b/Sources/Basics/Concurrency/ThrowingDefer.swift new file mode 100644 index 00000000000..fdf821f7c96 --- /dev/null +++ b/Sources/Basics/Concurrency/ThrowingDefer.swift @@ -0,0 +1,59 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2023-2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +/// Runs a cleanup closure (`deferred`) after a given `work` closure, +/// making sure `deferred` is run also when `work` throws an error. +/// - Parameters: +/// - work: The work that should be performed. Will always be executed. +/// - deferred: The cleanup that needs to be done in any case. +/// - Throws: Any error thrown by `deferred` or `work` (in that order). +/// - Returns: The result of `work`. +/// - Note: If `work` **and** `deferred` throw an error, +/// the one thrown by `deferred` is thrown from this function. +/// - SeeAlso: ``withAsyncThrowing(do:defer:)`` +public func withThrowing( + do work: () throws -> T, + defer deferred: () throws -> Void +) throws -> T { + do { + let result = try work() + try deferred() + return result + } catch { + try deferred() + throw error + } +} + +/// Runs an async cleanup closure (`deferred`) after a given async `work` closure, +/// making sure `deferred` is run also when `work` throws an error. +/// - Parameters: +/// - work: The work that should be performed. Will always be executed. +/// - deferred: The cleanup that needs to be done in any case. +/// - Throws: Any error thrown by `deferred` or `work` (in that order). +/// - Returns: The result of `work`. +/// - Note: If `work` **and** `deferred` throw an error, +/// the one thrown by `deferred` is thrown from this function. +/// - SeeAlso: ``withThrowing(do:defer:)`` +public func withAsyncThrowing( + do work: @Sendable () async throws -> T, + defer deferred: @Sendable () async throws -> Void +) async throws -> T { + do { + let result = try await work() + try await deferred() + return result + } catch { + try await deferred() + throw error + } +} diff --git a/Sources/Basics/SQLiteBackedCache.swift b/Sources/Basics/SQLiteBackedCache.swift index 73554819747..abb269b453b 100644 --- a/Sources/Basics/SQLiteBackedCache.swift +++ b/Sources/Basics/SQLiteBackedCache.swift @@ -84,8 +84,8 @@ public final class SQLiteBackedCache: Closable { } } - public func put( - key: Key, + private func put( + rawKey key: SQLite.SQLiteValue, value: Value, replace: Bool = false, observabilityScope: ObservabilityScope? = nil @@ -95,7 +95,7 @@ public final class SQLiteBackedCache: Closable { try self.executeStatement(query) { statement in let data = try self.jsonEncoder.encode(value) let bindings: [SQLite.SQLiteValue] = [ - .string(key), + key, .blob(data), ] try statement.bind(bindings) @@ -107,17 +107,39 @@ public final class SQLiteBackedCache: Closable { } observabilityScope? .emit( - warning: "truncating \(self.tableName) cache database since it reached max size of \(self.configuration.maxSizeInBytes ?? 0) bytes" + warning: """ + truncating \(self.tableName) cache database since it reached max size of \( + self.configuration.maxSizeInBytes ?? 0 + ) bytes + """ ) try self.executeStatement("DELETE FROM \(self.tableName);") { statement in try statement.step() } - try self.put(key: key, value: value, replace: replace, observabilityScope: observabilityScope) + try self.put(rawKey: key, value: value, replace: replace, observabilityScope: observabilityScope) } catch { throw error } } + public func put( + blobKey key: some Sequence, + value: Value, + replace: Bool = false, + observabilityScope: ObservabilityScope? = nil + ) throws { + try self.put(rawKey: .blob(Data(key)), value: value, observabilityScope: observabilityScope) + } + + public func put( + key: Key, + value: Value, + replace: Bool = false, + observabilityScope: ObservabilityScope? = nil + ) throws { + try self.put(rawKey: .string(key), value: value, replace: replace, observabilityScope: observabilityScope) + } + public func get(key: Key) throws -> Value? { let query = "SELECT value FROM \(self.tableName) WHERE key = ? LIMIT 1;" return try self.executeStatement(query) { statement -> Value? in @@ -129,6 +151,17 @@ public final class SQLiteBackedCache: Closable { } } + public func get(blobKey key: some Sequence) throws -> Value? { + let query = "SELECT value FROM \(self.tableName) WHERE key = ? LIMIT 1;" + return try self.executeStatement(query) { statement -> Value? in + try statement.bind([.blob(Data(key))]) + let data = try statement.step()?.blob(at: 0) + return try data.flatMap { + try self.jsonDecoder.decode(Value.self, from: $0) + } + } + } + public func remove(key: Key) throws { let query = "DELETE FROM \(self.tableName) WHERE key = ?;" try self.executeStatement(query) { statement in @@ -143,7 +176,7 @@ public final class SQLiteBackedCache: Closable { let result: Result let statement = try db.prepare(query: query) do { - result = .success(try body(statement)) + result = try .success(body(statement)) } catch { result = .failure(error) } diff --git a/Sources/QueryEngine/FileSystem/AsyncFileSystem.swift b/Sources/QueryEngine/FileSystem/AsyncFileSystem.swift new file mode 100644 index 00000000000..9b461490260 --- /dev/null +++ b/Sources/QueryEngine/FileSystem/AsyncFileSystem.swift @@ -0,0 +1,37 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2023-2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import protocol _Concurrency.Actor +import protocol Crypto.HashFunction +import struct SystemPackage.Errno +import struct SystemPackage.FilePath + +public protocol AsyncFileSystem: Actor { + func withOpenReadableFile(_ path: FilePath, _ body: (OpenReadableFile) async throws -> T) async throws -> T + func withOpenWritableFile(_ path: FilePath, _ body: (OpenWritableFile) async throws -> T) async throws -> T +} + +enum FileSystemError: Error { + case fileDoesNotExist(FilePath) + case bufferLimitExceeded(FilePath) + case systemError(FilePath, Errno) +} + +extension Error { + func attach(path: FilePath) -> any Error { + if let error = self as? Errno { + FileSystemError.systemError(path, error) + } else { + self + } + } +} diff --git a/Sources/QueryEngine/FileSystem/FileCacheRecord.swift b/Sources/QueryEngine/FileSystem/FileCacheRecord.swift new file mode 100644 index 00000000000..18f42c29f58 --- /dev/null +++ b/Sources/QueryEngine/FileSystem/FileCacheRecord.swift @@ -0,0 +1,39 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2023-2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +// FIXME: need a new swift-system tag to remove `@preconcurrency` +@preconcurrency import struct SystemPackage.FilePath + +public struct FileCacheRecord: Sendable { + public let path: FilePath + public let hash: String +} + +extension FileCacheRecord: Codable { + enum CodingKeys: CodingKey { + case path + case hash + } + + // FIXME: `Codable` on `FilePath` is broken, thus all `Codable` types with `FilePath` properties need a custom impl. + public init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.path = try FilePath(container.decode(String.self, forKey: .path)) + self.hash = try container.decode(String.self, forKey: .hash) + } + + public func encode(to encoder: any Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(self.path.string, forKey: .path) + try container.encode(self.hash, forKey: .hash) + } +} diff --git a/Sources/QueryEngine/FileSystem/OpenReadableFile.swift b/Sources/QueryEngine/FileSystem/OpenReadableFile.swift new file mode 100644 index 00000000000..e4bdaa1fda8 --- /dev/null +++ b/Sources/QueryEngine/FileSystem/OpenReadableFile.swift @@ -0,0 +1,54 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2023-2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import protocol Crypto.HashFunction +import struct SystemPackage.FileDescriptor + +public struct OpenReadableFile { + let readChunkSize: Int + + enum FileHandle { + case local(FileDescriptor) + case virtual([UInt8]) + } + + let fileHandle: FileHandle + + func read() async throws -> ReadableFileStream { + switch self.fileHandle { + case .local(let fileDescriptor): + ReadableFileStream.local(.init(fileDescriptor: fileDescriptor, readChunkSize: self.readChunkSize)) + case .virtual(let array): + ReadableFileStream.virtual(.init(bytes: array)) + } + } + + func hash(with hashFunction: inout some HashFunction) async throws { + switch self.fileHandle { + case .local(let fileDescriptor): + var buffer = [UInt8](repeating: 0, count: readChunkSize) + var bytesRead = 0 + repeat { + bytesRead = try buffer.withUnsafeMutableBytes { + try fileDescriptor.read(into: $0) + } + + if bytesRead > 0 { + hashFunction.update(data: buffer[0 ..< bytesRead]) + } + + } while bytesRead > 0 + case .virtual(let array): + hashFunction.update(data: array) + } + } +} diff --git a/Sources/QueryEngine/FileSystem/OpenWritableFile.swift b/Sources/QueryEngine/FileSystem/OpenWritableFile.swift new file mode 100644 index 00000000000..a8f4da00416 --- /dev/null +++ b/Sources/QueryEngine/FileSystem/OpenWritableFile.swift @@ -0,0 +1,32 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import struct SystemPackage.FileDescriptor +import struct SystemPackage.FilePath + +public struct OpenWritableFile { + enum FileHandle { + case local(FileDescriptor) + case virtual(VirtualFileSystem.Storage, FilePath) + } + + let fileHandle: FileHandle + + func write(_ bytes: some Sequence) async throws { + switch self.fileHandle { + case .local(let fileDescriptor): + _ = try fileDescriptor.writeAll(bytes) + case .virtual(let storage, let path): + storage.content[path, default: []].append(contentsOf: bytes) + } + } +} diff --git a/Sources/QueryEngine/FileSystem/ReadableFileStream.swift b/Sources/QueryEngine/FileSystem/ReadableFileStream.swift new file mode 100644 index 00000000000..5b3628f5a89 --- /dev/null +++ b/Sources/QueryEngine/FileSystem/ReadableFileStream.swift @@ -0,0 +1,104 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import _Concurrency +import SystemPackage + +public enum ReadableFileStream: AsyncSequence { + public typealias Element = [UInt8] + + case local(LocalReadableFileStream) + case virtual(VirtualReadableFileStream) + + public enum Iterator: AsyncIteratorProtocol { + case local(LocalReadableFileStream.Iterator) + case virtual(VirtualReadableFileStream.Iterator) + + public func next() async throws -> [UInt8]? { + switch self { + case .local(let local): + try await local.next() + case .virtual(let virtual): + try await virtual.next() + } + } + } + + public func makeAsyncIterator() -> Iterator { + switch self { + case .local(let local): + .local(local.makeAsyncIterator()) + case .virtual(let virtual): + .virtual(virtual.makeAsyncIterator()) + } + } +} + +public struct LocalReadableFileStream: AsyncSequence { + public typealias Element = [UInt8] + + let fileDescriptor: FileDescriptor + let readChunkSize: Int + + public final class Iterator: AsyncIteratorProtocol { + init(_ fileDescriptor: FileDescriptor, readChunkSize: Int) { + self.fileDescriptor = fileDescriptor + self.readChunkSize = readChunkSize + } + + private let fileDescriptor: FileDescriptor + private let readChunkSize: Int + + public func next() async throws -> [UInt8]? { + var buffer = [UInt8](repeating: 0, count: readChunkSize) + + let bytesRead = try buffer.withUnsafeMutableBytes { + try self.fileDescriptor.read(into: $0) + } + + guard bytesRead > 0 else { + return nil + } + + buffer.removeLast(self.readChunkSize - bytesRead) + return buffer + } + } + + public func makeAsyncIterator() -> Iterator { + Iterator(self.fileDescriptor, readChunkSize: self.readChunkSize) + } +} + +public struct VirtualReadableFileStream: AsyncSequence { + public typealias Element = [UInt8] + + public final class Iterator: AsyncIteratorProtocol { + init(bytes: [UInt8]? = nil) { + self.bytes = bytes + } + + var bytes: [UInt8]? + + public func next() async throws -> [UInt8]? { + defer { bytes = nil } + + return self.bytes + } + } + + let bytes: [UInt8] + + public func makeAsyncIterator() -> Iterator { + Iterator(bytes: self.bytes) + } +} diff --git a/Sources/QueryEngine/FileSystem/VirtualFileSystem.swift b/Sources/QueryEngine/FileSystem/VirtualFileSystem.swift new file mode 100644 index 00000000000..581e0776aa9 --- /dev/null +++ b/Sources/QueryEngine/FileSystem/VirtualFileSystem.swift @@ -0,0 +1,45 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2023-2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import struct SystemPackage.FilePath + +actor VirtualFileSystem: AsyncFileSystem { + public static let defaultChunkSize = 512 * 1024 + + let readChunkSize: Int + + final class Storage { + init(_ content: [FilePath: [UInt8]]) { + self.content = content + } + + var content: [FilePath: [UInt8]] + } + + private let storage: Storage + + init(content: [FilePath: [UInt8]] = [:], readChunkSize: Int = defaultChunkSize) { + self.storage = .init(content) + self.readChunkSize = readChunkSize + } + + func withOpenReadableFile(_ path: FilePath, _ body: (OpenReadableFile) async throws -> T) async throws -> T { + guard let bytes = storage.content[path] else { + throw FileSystemError.fileDoesNotExist(path) + } + return try await body(.init(readChunkSize: self.readChunkSize, fileHandle: .virtual(bytes))) + } + + func withOpenWritableFile(_ path: FilePath, _ body: (OpenWritableFile) async throws -> T) async throws -> T { + try await body(.init(fileHandle: .virtual(self.storage, path))) + } +} diff --git a/Sources/QueryEngine/Query.swift b/Sources/QueryEngine/Query.swift new file mode 100644 index 00000000000..536db5d22a6 --- /dev/null +++ b/Sources/QueryEngine/Query.swift @@ -0,0 +1,23 @@ +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2023-2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Crypto +import struct SystemPackage.FilePath + +public protocol Query: Encodable { + func run(engine: QueryEngine) async throws -> FilePath +} + +extension Query { + func hash(with hashFunction: inout some HashFunction) { + fatalError("\(#function) not implemented") + } +} diff --git a/Sources/QueryEngine/QueryEngine.swift b/Sources/QueryEngine/QueryEngine.swift new file mode 100644 index 00000000000..a6536ac9892 --- /dev/null +++ b/Sources/QueryEngine/QueryEngine.swift @@ -0,0 +1,116 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2023-2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Basics +import Crypto + +public func withQueryEngine( + _ fileSystem: any AsyncFileSystem, + _ observabilityScope: ObservabilityScope, + cacheLocation: SQLite.Location, + _ body: @Sendable (QueryEngine) async throws -> Void +) async throws { + let engine = QueryEngine( + fileSystem, + observabilityScope, + cacheLocation: cacheLocation + ) + + try await withAsyncThrowing { + try await body(engine) + } defer: { + try await engine.shutDown() + } +} + +/// Cacheable computations engine. Currently the engine makes an assumption that computations produce same results for +/// the same query values and write results to a single file path. +public actor QueryEngine { + private(set) var cacheHits = 0 + private(set) var cacheMisses = 0 + + public let fileSystem: any AsyncFileSystem + public let httpClient = HTTPClient() + public let observabilityScope: ObservabilityScope + private let resultsCache: SQLiteBackedCache + private var isShutDown = false + + /// Creates a new instance of the ``QueryEngine`` actor. Requires an explicit call + /// to ``QueryEngine//shutdown`` before the instance is deinitialized. The recommended approach to resource + /// management is to place `engine.shutDown()` when the engine is no longer used, but is not deinitialized yet. + /// - Parameter fileSystem: Implementation of a file system this engine should use. + /// - Parameter cacheLocation: Location of cache storage used by the engine. + /// - Parameter logger: Logger to use during queries execution. + init( + _ fileSystem: any AsyncFileSystem, + _ observabilityScope: ObservabilityScope, + cacheLocation: SQLite.Location + ) { + self.fileSystem = fileSystem + self.observabilityScope = observabilityScope + self.resultsCache = SQLiteBackedCache(tableName: "cache_table", location: cacheLocation) + } + + public func shutDown() async throws { + precondition(!self.isShutDown, "`QueryEngine/shutDown` should be called only once") + try self.resultsCache.close() + + self.isShutDown = true + } + + deinit { + let isShutDown = self.isShutDown + precondition( + isShutDown, + "`QueryEngine/shutDown` should be called explicitly on instances of `Engine` before deinitialization" + ) + } + + /// Executes a given query if no cached result of it is available. Otherwise fetches the result from engine's cache. + /// - Parameter query: A query value to execute. + /// - Returns: A file path to query's result recorded in a file. + public subscript(_ query: some Query) -> FileCacheRecord { + get async throws { + var hashFunction = SHA512() + query.hash(with: &hashFunction) + let key = hashFunction.finalize() + + if let fileRecord = try resultsCache.get(blobKey: key) { + hashFunction = SHA512() + try await self.fileSystem.withOpenReadableFile(fileRecord.path) { + try await $0.hash(with: &hashFunction) + } + let fileHash = hashFunction.finalize().description + + if fileHash == fileRecord.hash { + self.cacheHits += 1 + return fileRecord + } + } + + self.cacheMisses += 1 + let resultPath = try await query.run(engine: self) + hashFunction = SHA512() + + try await self.fileSystem.withOpenReadableFile(resultPath) { + try await $0.hash(with: &hashFunction) + } + let resultHash = hashFunction.finalize() + let result = FileCacheRecord(path: resultPath, hash: resultHash.description) + + // FIXME: update `SQLiteBackedCache` to store `resultHash` directly instead of relying on string conversions + try self.resultsCache.put(blobKey: key, value: result) + + return result + } + } +} From e79066a4ed894625e77a11351c1d1f4d9fecebed Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 11 Apr 2024 01:33:41 +0100 Subject: [PATCH 077/159] Add `HashEncoder` and `QueryEngineTests` (#7435) Depends on https://github.com/apple/swift-package-manager/pull/7347. Previously, `QueryEngine` couldn't cache anything due to a missing hashing implementation that could be consistent across SwiftPM relaunches. Here `HashEncoder` is implemented on top `Codable`. While it probably has certain performance overhead when compared to an implementation with macros, it should be good enough for prototyping. New `QueryEngine` now works for trivial computations, as verified by newly added `QueryEngineTests`. --- Package.swift | 11 +- Sources/Basics/SQLite.swift | 66 ++--- Sources/Basics/SQLiteBackedCache.swift | 40 +-- Sources/QueryEngine/CacheKey.swift | 169 ++++++++++++ .../FileSystem/AsyncFileSystem.swift | 13 +- .../FileSystem/FileCacheRecord.swift | 10 +- .../FileSystem/OpenReadableFile.swift | 2 +- .../FileSystem/OpenWritableFile.swift | 4 +- .../FileSystem/ReadableFileStream.swift | 30 +-- .../FileSystem/VirtualFileSystem.swift | 12 +- Sources/QueryEngine/Query.swift | 251 +++++++++++++++++- Sources/QueryEngine/QueryEngine.swift | 36 +-- Tests/QueryEngineTests/QueryEngineTests.swift | 153 +++++++++++ 13 files changed, 692 insertions(+), 105 deletions(-) create mode 100644 Sources/QueryEngine/CacheKey.swift create mode 100644 Tests/QueryEngineTests/QueryEngineTests.swift diff --git a/Package.swift b/Package.swift index b8f4923719d..ab065fca776 100644 --- a/Package.swift +++ b/Package.swift @@ -359,7 +359,7 @@ let package = Package( /** Support for building using Xcode's build system */ name: "XCBuildSupport", dependencies: ["DriverSupport", "SPMBuildCore", "PackageGraph"], - exclude: ["CMakeLists.txt", "CODEOWNERS"] + exclude: ["CMakeLists.txt"] ), .target( /** High level functionality */ @@ -473,6 +473,9 @@ let package = Package( dependencies: [ "Basics", .product(name: "Crypto", package: "swift-crypto"), + ], + swiftSettings: [ + .enableExperimentalFeature("StrictConcurrency=complete"), ] ), @@ -533,7 +536,7 @@ let package = Package( "Commands", "SwiftSDKCommand", "PackageCollectionsCommand", - "PackageRegistryCommand" + "PackageRegistryCommand", ], linkerSettings: swiftpmLinkSettings ), @@ -672,6 +675,10 @@ let package = Package( name: "PackageSigningTests", dependencies: ["SPMTestSupport", "PackageSigning"] ), + .testTarget( + name: "QueryEngineTests", + dependencies: ["QueryEngine", "SPMTestSupport"] + ), .testTarget( name: "SourceControlTests", dependencies: ["SourceControl", "SPMTestSupport"], diff --git a/Sources/Basics/SQLite.swift b/Sources/Basics/SQLite.swift index ab4e8ae34a5..d6cc6a108d2 100644 --- a/Sources/Basics/SQLite.swift +++ b/Sources/Basics/SQLite.swift @@ -19,12 +19,12 @@ import SPMSQLite3 #endif /// A minimal SQLite wrapper. -public final class SQLite { +package final class SQLite { /// The location of the database. - public let location: Location + package let location: Location /// The configuration for the database. - public let configuration: Configuration + package let configuration: Configuration /// Pointer to the database. let db: OpaquePointer @@ -32,7 +32,7 @@ public final class SQLite { /// Create or open the database at the given path. /// /// The database is opened in serialized mode. - public init(location: Location, configuration: Configuration = Configuration()) throws { + package init(location: Location, configuration: Configuration = Configuration()) throws { self.location = location self.configuration = configuration @@ -64,19 +64,19 @@ public final class SQLite { } @available(*, deprecated, message: "use init(location:configuration) instead") - public convenience init(dbPath: AbsolutePath) throws { + package convenience init(dbPath: AbsolutePath) throws { try self.init(location: .path(dbPath)) } /// Prepare the given query. - public func prepare(query: String) throws -> PreparedStatement { + package func prepare(query: String) throws -> PreparedStatement { try PreparedStatement(db: self.db, query: query) } /// Directly execute the given query. /// /// Note: Use withCString for string arguments. - public func exec(query queryString: String, args: [CVarArg] = [], _ callback: SQLiteExecCallback? = nil) throws { + package func exec(query queryString: String, args: [CVarArg] = [], _ callback: SQLiteExecCallback? = nil) throws { let query = withVaList(args) { ptr in sqlite3_vmprintf(queryString, ptr) } @@ -96,27 +96,27 @@ public final class SQLite { } } - public func close() throws { + package func close() throws { try Self.checkError { sqlite3_close(db) } } - public typealias SQLiteExecCallback = ([Column]) -> Void + package typealias SQLiteExecCallback = ([Column]) -> Void - public struct Configuration { - public var busyTimeoutMilliseconds: Int32 - public var maxSizeInBytes: Int? + package struct Configuration { + package var busyTimeoutMilliseconds: Int32 + package var maxSizeInBytes: Int? // https://www.sqlite.org/pgszchng2016.html private let defaultPageSizeInBytes = 1024 - public init() { + package init() { self.busyTimeoutMilliseconds = 5000 self.maxSizeInBytes = .none } // FIXME: deprecated 12/2020, remove once clients migrated over @available(*, deprecated, message: "use busyTimeout instead") - public var busyTimeoutSeconds: Int32 { + package var busyTimeoutSeconds: Int32 { get { self._busyTimeoutSeconds } set { @@ -133,7 +133,7 @@ public final class SQLite { } } - public var maxSizeInMegabytes: Int? { + package var maxSizeInMegabytes: Int? { get { self.maxSizeInBytes.map { $0 / (1024 * 1024) } } @@ -142,12 +142,12 @@ public final class SQLite { } } - public var maxPageCount: Int? { + package var maxPageCount: Int? { self.maxSizeInBytes.map { $0 / self.defaultPageSizeInBytes } } } - public enum Location { + package enum Location: Sendable { case path(AbsolutePath) case memory case temporary @@ -165,7 +165,7 @@ public final class SQLite { } /// Represents an sqlite value. - public enum SQLiteValue { + package enum SQLiteValue { case null case string(String) case int(Int) @@ -173,35 +173,35 @@ public final class SQLite { } /// Represents a row returned by called step() on a prepared statement. - public struct Row { + package struct Row { /// The pointer to the prepared statement. let stmt: OpaquePointer /// Get integer at the given column index. - public func int(at index: Int32) -> Int { + package func int(at index: Int32) -> Int { Int(sqlite3_column_int64(self.stmt, index)) } /// Get blob data at the given column index. - public func blob(at index: Int32) -> Data { + package func blob(at index: Int32) -> Data { let bytes = sqlite3_column_blob(stmt, index)! let count = sqlite3_column_bytes(stmt, index) return Data(bytes: bytes, count: Int(count)) } /// Get string at the given column index. - public func string(at index: Int32) -> String { + package func string(at index: Int32) -> String { String(cString: sqlite3_column_text(self.stmt, index)) } } - public struct Column { - public var name: String - public var value: String + package struct Column { + package var name: String + package var value: String } /// Represents a prepared statement. - public struct PreparedStatement { + package struct PreparedStatement { typealias sqlite3_destructor_type = @convention(c) (UnsafeMutableRawPointer?) -> Void static let SQLITE_STATIC = unsafeBitCast(0, to: sqlite3_destructor_type.self) static let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self) @@ -209,7 +209,7 @@ public final class SQLite { /// The pointer to the prepared statement. let stmt: OpaquePointer - public init(db: OpaquePointer, query: String) throws { + package init(db: OpaquePointer, query: String) throws { var stmt: OpaquePointer? try SQLite.checkError { sqlite3_prepare_v2(db, query, -1, &stmt, nil) } self.stmt = stmt! @@ -217,7 +217,7 @@ public final class SQLite { /// Evaluate the prepared statement. @discardableResult - public func step() throws -> Row? { + package func step() throws -> Row? { let result = sqlite3_step(stmt) switch result { @@ -231,7 +231,7 @@ public final class SQLite { } /// Bind the given arguments to the statement. - public func bind(_ arguments: [SQLiteValue]) throws { + package func bind(_ arguments: [SQLiteValue]) throws { for (idx, argument) in arguments.enumerated() { let idx = Int32(idx) + 1 switch argument { @@ -258,17 +258,17 @@ public final class SQLite { } /// Reset the prepared statement. - public func reset() throws { + package func reset() throws { try SQLite.checkError { sqlite3_reset(stmt) } } /// Clear bindings from the prepared statement. - public func clearBindings() throws { + package func clearBindings() throws { try SQLite.checkError { sqlite3_clear_bindings(stmt) } } /// Finalize the statement and free up resources. - public func finalize() throws { + package func finalize() throws { try SQLite.checkError { sqlite3_finalize(stmt) } } } @@ -296,7 +296,7 @@ public final class SQLite { } } - public enum Errors: Error { + package enum Errors: Error { case databaseFull } } diff --git a/Sources/Basics/SQLiteBackedCache.swift b/Sources/Basics/SQLiteBackedCache.swift index abb269b453b..373755bd39b 100644 --- a/Sources/Basics/SQLiteBackedCache.swift +++ b/Sources/Basics/SQLiteBackedCache.swift @@ -17,13 +17,13 @@ import class TSCBasic.InMemoryFileSystem import var TSCBasic.localFileSystem /// SQLite backed persistent cache. -public final class SQLiteBackedCache: Closable { - public typealias Key = String +package final class SQLiteBackedCache: Closable { + package typealias Key = String - public let tableName: String - public let fileSystem: FileSystem - public let location: SQLite.Location - public let configuration: SQLiteBackedCacheConfiguration + package let tableName: String + package let fileSystem: FileSystem + package let location: SQLite.Location + package let configuration: SQLiteBackedCacheConfiguration private var state = State.idle private let stateLock = NSLock() @@ -37,7 +37,7 @@ public final class SQLiteBackedCache: Closable { /// - tableName: The SQLite table name. Must follow SQLite naming rules (e.g., no spaces). /// - location: SQLite.Location /// - configuration: Optional. Configuration for the cache. - public init(tableName: String, location: SQLite.Location, configuration: SQLiteBackedCacheConfiguration = .init()) { + package init(tableName: String, location: SQLite.Location, configuration: SQLiteBackedCacheConfiguration = .init()) { self.tableName = tableName self.location = location switch self.location { @@ -57,7 +57,7 @@ public final class SQLiteBackedCache: Closable { /// - tableName: The SQLite table name. Must follow SQLite naming rules (e.g., no spaces). /// - path: The path of the SQLite database. /// - configuration: Optional. Configuration for the cache. - public convenience init( + package convenience init( tableName: String, path: AbsolutePath, configuration: SQLiteBackedCacheConfiguration = .init() @@ -75,7 +75,7 @@ public final class SQLiteBackedCache: Closable { } } - public func close() throws { + package func close() throws { try self.withStateLock { if case .connected(let db) = self.state { try db.close() @@ -122,7 +122,7 @@ public final class SQLiteBackedCache: Closable { } } - public func put( + package func put( blobKey key: some Sequence, value: Value, replace: Bool = false, @@ -131,7 +131,7 @@ public final class SQLiteBackedCache: Closable { try self.put(rawKey: .blob(Data(key)), value: value, observabilityScope: observabilityScope) } - public func put( + package func put( key: Key, value: Value, replace: Bool = false, @@ -140,7 +140,7 @@ public final class SQLiteBackedCache: Closable { try self.put(rawKey: .string(key), value: value, replace: replace, observabilityScope: observabilityScope) } - public func get(key: Key) throws -> Value? { + package func get(key: Key) throws -> Value? { let query = "SELECT value FROM \(self.tableName) WHERE key = ? LIMIT 1;" return try self.executeStatement(query) { statement -> Value? in try statement.bind([.string(key)]) @@ -151,7 +151,7 @@ public final class SQLiteBackedCache: Closable { } } - public func get(blobKey key: some Sequence) throws -> Value? { + package func get(blobKey key: some Sequence) throws -> Value? { let query = "SELECT value FROM \(self.tableName) WHERE key = ? LIMIT 1;" return try self.executeStatement(query) { statement -> Value? in try statement.bind([.blob(Data(key))]) @@ -162,7 +162,7 @@ public final class SQLiteBackedCache: Closable { } } - public func remove(key: Key) throws { + package func remove(key: Key) throws { let query = "DELETE FROM \(self.tableName) WHERE key = ?;" try self.executeStatement(query) { statement in try statement.bind([.string(key)]) @@ -254,12 +254,12 @@ public final class SQLiteBackedCache: Closable { } } -public struct SQLiteBackedCacheConfiguration { - public var truncateWhenFull: Bool +package struct SQLiteBackedCacheConfiguration { + package var truncateWhenFull: Bool fileprivate var underlying: SQLite.Configuration - public init() { + package init() { self.underlying = .init() self.truncateWhenFull = true self.maxSizeInMegabytes = 100 @@ -267,7 +267,7 @@ public struct SQLiteBackedCacheConfiguration { self.busyTimeoutMilliseconds = 1000 } - public var maxSizeInMegabytes: Int? { + package var maxSizeInMegabytes: Int? { get { self.underlying.maxSizeInMegabytes } @@ -276,7 +276,7 @@ public struct SQLiteBackedCacheConfiguration { } } - public var maxSizeInBytes: Int? { + package var maxSizeInBytes: Int? { get { self.underlying.maxSizeInBytes } @@ -285,7 +285,7 @@ public struct SQLiteBackedCacheConfiguration { } } - public var busyTimeoutMilliseconds: Int32 { + package var busyTimeoutMilliseconds: Int32 { get { self.underlying.busyTimeoutMilliseconds } diff --git a/Sources/QueryEngine/CacheKey.swift b/Sources/QueryEngine/CacheKey.swift new file mode 100644 index 00000000000..2fb5fa174fa --- /dev/null +++ b/Sources/QueryEngine/CacheKey.swift @@ -0,0 +1,169 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +@_exported import protocol Crypto.HashFunction +import struct Foundation.URL +import struct SystemPackage.FilePath + +/// Indicates that values of a conforming type can be hashed with an arbitrary hashing function. Unlike `Hashable`, +/// this protocol doesn't utilize random seed values and produces consistent hash values across process launches. +package protocol CacheKey: Encodable { +} + +extension Bool: CacheKey { + func hash(with hashFunction: inout some HashFunction) { + String(reflecting: Self.self).hash(with: &hashFunction) + hashFunction.update(data: self ? [1] : [0]) + } +} + +extension Int: CacheKey { + func hash(with hashFunction: inout some HashFunction) { + String(reflecting: Self.self).hash(with: &hashFunction) + withUnsafeBytes(of: self) { + hashFunction.update(data: $0) + } + } +} + +extension Int8: CacheKey { + func hash(with hashFunction: inout some HashFunction) { + String(reflecting: Self.self).hash(with: &hashFunction) + withUnsafeBytes(of: self) { + hashFunction.update(data: $0) + } + } +} + +extension Int16: CacheKey { + func hash(with hashFunction: inout some HashFunction) { + String(reflecting: Self.self).hash(with: &hashFunction) + withUnsafeBytes(of: self) { + hashFunction.update(data: $0) + } + } +} + +extension Int32: CacheKey { + func hash(with hashFunction: inout some HashFunction) { + String(reflecting: Self.self).hash(with: &hashFunction) + withUnsafeBytes(of: self) { + hashFunction.update(data: $0) + } + } +} + +extension Int64: CacheKey { + func hash(with hashFunction: inout some HashFunction) { + String(reflecting: Self.self).hash(with: &hashFunction) + withUnsafeBytes(of: self) { + hashFunction.update(data: $0) + } + } +} + +extension UInt: CacheKey { + func hash(with hashFunction: inout some HashFunction) { + String(reflecting: Self.self).hash(with: &hashFunction) + withUnsafeBytes(of: self) { + hashFunction.update(data: $0) + } + } +} + +extension UInt8: CacheKey { + func hash(with hashFunction: inout some HashFunction) { + String(reflecting: Self.self).hash(with: &hashFunction) + withUnsafeBytes(of: self) { + hashFunction.update(data: $0) + } + } +} + +extension UInt16: CacheKey { + func hash(with hashFunction: inout some HashFunction) { + String(reflecting: Self.self).hash(with: &hashFunction) + withUnsafeBytes(of: self) { + hashFunction.update(data: $0) + } + } +} + +extension UInt32: CacheKey { + func hash(with hashFunction: inout some HashFunction) { + String(reflecting: Self.self).hash(with: &hashFunction) + withUnsafeBytes(of: self) { + hashFunction.update(data: $0) + } + } +} + +extension UInt64: CacheKey { + func hash(with hashFunction: inout some HashFunction) { + String(reflecting: Self.self).hash(with: &hashFunction) + withUnsafeBytes(of: self) { + hashFunction.update(data: $0) + } + } +} + +extension Float: CacheKey { + func hash(with hashFunction: inout some HashFunction) { + String(reflecting: Self.self).hash(with: &hashFunction) + withUnsafeBytes(of: self) { + hashFunction.update(data: $0) + } + } +} + +extension Double: CacheKey { + func hash(with hashFunction: inout some HashFunction) { + String(reflecting: Self.self).hash(with: &hashFunction) + withUnsafeBytes(of: self) { + hashFunction.update(data: $0) + } + } +} + +extension String: CacheKey { + func hash(with hashFunction: inout some HashFunction) { + var t = String(reflecting: Self.self) + t.withUTF8 { + hashFunction.update(data: $0) + } + var x = self + x.withUTF8 { + hashFunction.update(data: $0) + } + } +} + +extension FilePath: CacheKey { + func hash(with hashFunction: inout some HashFunction) { + String(reflecting: Self.self).hash(with: &hashFunction) + self.string.hash(with: &hashFunction) + } +} + +extension FilePath.Component: CacheKey { + func hash(with hashFunction: inout some HashFunction) { + String(reflecting: Self.self).hash(with: &hashFunction) + self.string.hash(with: &hashFunction) + } +} + +extension URL: CacheKey { + func hash(with hashFunction: inout some HashFunction) { + String(reflecting: Self.self).hash(with: &hashFunction) + self.description.hash(with: &hashFunction) + } +} diff --git a/Sources/QueryEngine/FileSystem/AsyncFileSystem.swift b/Sources/QueryEngine/FileSystem/AsyncFileSystem.swift index 9b461490260..02e82ca5593 100644 --- a/Sources/QueryEngine/FileSystem/AsyncFileSystem.swift +++ b/Sources/QueryEngine/FileSystem/AsyncFileSystem.swift @@ -15,9 +15,16 @@ import protocol Crypto.HashFunction import struct SystemPackage.Errno import struct SystemPackage.FilePath -public protocol AsyncFileSystem: Actor { - func withOpenReadableFile(_ path: FilePath, _ body: (OpenReadableFile) async throws -> T) async throws -> T - func withOpenWritableFile(_ path: FilePath, _ body: (OpenWritableFile) async throws -> T) async throws -> T +package protocol AsyncFileSystem: Actor { + func withOpenReadableFile( + _ path: FilePath, + _ body: @Sendable (OpenReadableFile) async throws -> T + ) async throws -> T + + func withOpenWritableFile( + _ path: FilePath, + _ body: @Sendable (OpenWritableFile) async throws -> T + ) async throws -> T } enum FileSystemError: Error { diff --git a/Sources/QueryEngine/FileSystem/FileCacheRecord.swift b/Sources/QueryEngine/FileSystem/FileCacheRecord.swift index 18f42c29f58..ed23fd78bb5 100644 --- a/Sources/QueryEngine/FileSystem/FileCacheRecord.swift +++ b/Sources/QueryEngine/FileSystem/FileCacheRecord.swift @@ -13,9 +13,9 @@ // FIXME: need a new swift-system tag to remove `@preconcurrency` @preconcurrency import struct SystemPackage.FilePath -public struct FileCacheRecord: Sendable { - public let path: FilePath - public let hash: String +package struct FileCacheRecord: Sendable { + package let path: FilePath + package let hash: String } extension FileCacheRecord: Codable { @@ -25,13 +25,13 @@ extension FileCacheRecord: Codable { } // FIXME: `Codable` on `FilePath` is broken, thus all `Codable` types with `FilePath` properties need a custom impl. - public init(from decoder: any Decoder) throws { + package init(from decoder: any Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.path = try FilePath(container.decode(String.self, forKey: .path)) self.hash = try container.decode(String.self, forKey: .hash) } - public func encode(to encoder: any Encoder) throws { + package func encode(to encoder: any Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.path.string, forKey: .path) try container.encode(self.hash, forKey: .hash) diff --git a/Sources/QueryEngine/FileSystem/OpenReadableFile.swift b/Sources/QueryEngine/FileSystem/OpenReadableFile.swift index e4bdaa1fda8..dae8d37fd87 100644 --- a/Sources/QueryEngine/FileSystem/OpenReadableFile.swift +++ b/Sources/QueryEngine/FileSystem/OpenReadableFile.swift @@ -13,7 +13,7 @@ import protocol Crypto.HashFunction import struct SystemPackage.FileDescriptor -public struct OpenReadableFile { +package struct OpenReadableFile: Sendable { let readChunkSize: Int enum FileHandle { diff --git a/Sources/QueryEngine/FileSystem/OpenWritableFile.swift b/Sources/QueryEngine/FileSystem/OpenWritableFile.swift index a8f4da00416..be69f3cdc7c 100644 --- a/Sources/QueryEngine/FileSystem/OpenWritableFile.swift +++ b/Sources/QueryEngine/FileSystem/OpenWritableFile.swift @@ -13,8 +13,8 @@ import struct SystemPackage.FileDescriptor import struct SystemPackage.FilePath -public struct OpenWritableFile { - enum FileHandle { +package struct OpenWritableFile: Sendable { + enum FileHandle: Sendable { case local(FileDescriptor) case virtual(VirtualFileSystem.Storage, FilePath) } diff --git a/Sources/QueryEngine/FileSystem/ReadableFileStream.swift b/Sources/QueryEngine/FileSystem/ReadableFileStream.swift index 5b3628f5a89..f1fc7864ed5 100644 --- a/Sources/QueryEngine/FileSystem/ReadableFileStream.swift +++ b/Sources/QueryEngine/FileSystem/ReadableFileStream.swift @@ -13,17 +13,17 @@ import _Concurrency import SystemPackage -public enum ReadableFileStream: AsyncSequence { - public typealias Element = [UInt8] +package enum ReadableFileStream: AsyncSequence { + package typealias Element = [UInt8] case local(LocalReadableFileStream) case virtual(VirtualReadableFileStream) - public enum Iterator: AsyncIteratorProtocol { + package enum Iterator: AsyncIteratorProtocol { case local(LocalReadableFileStream.Iterator) case virtual(VirtualReadableFileStream.Iterator) - public func next() async throws -> [UInt8]? { + package func next() async throws -> [UInt8]? { switch self { case .local(let local): try await local.next() @@ -33,7 +33,7 @@ public enum ReadableFileStream: AsyncSequence { } } - public func makeAsyncIterator() -> Iterator { + package func makeAsyncIterator() -> Iterator { switch self { case .local(let local): .local(local.makeAsyncIterator()) @@ -43,13 +43,13 @@ public enum ReadableFileStream: AsyncSequence { } } -public struct LocalReadableFileStream: AsyncSequence { - public typealias Element = [UInt8] +package struct LocalReadableFileStream: AsyncSequence { + package typealias Element = [UInt8] let fileDescriptor: FileDescriptor let readChunkSize: Int - public final class Iterator: AsyncIteratorProtocol { + package final class Iterator: AsyncIteratorProtocol { init(_ fileDescriptor: FileDescriptor, readChunkSize: Int) { self.fileDescriptor = fileDescriptor self.readChunkSize = readChunkSize @@ -58,7 +58,7 @@ public struct LocalReadableFileStream: AsyncSequence { private let fileDescriptor: FileDescriptor private let readChunkSize: Int - public func next() async throws -> [UInt8]? { + package func next() async throws -> [UInt8]? { var buffer = [UInt8](repeating: 0, count: readChunkSize) let bytesRead = try buffer.withUnsafeMutableBytes { @@ -74,22 +74,22 @@ public struct LocalReadableFileStream: AsyncSequence { } } - public func makeAsyncIterator() -> Iterator { + package func makeAsyncIterator() -> Iterator { Iterator(self.fileDescriptor, readChunkSize: self.readChunkSize) } } -public struct VirtualReadableFileStream: AsyncSequence { - public typealias Element = [UInt8] +package struct VirtualReadableFileStream: AsyncSequence { + package typealias Element = [UInt8] - public final class Iterator: AsyncIteratorProtocol { + package final class Iterator: AsyncIteratorProtocol { init(bytes: [UInt8]? = nil) { self.bytes = bytes } var bytes: [UInt8]? - public func next() async throws -> [UInt8]? { + package func next() async throws -> [UInt8]? { defer { bytes = nil } return self.bytes @@ -98,7 +98,7 @@ public struct VirtualReadableFileStream: AsyncSequence { let bytes: [UInt8] - public func makeAsyncIterator() -> Iterator { + package func makeAsyncIterator() -> Iterator { Iterator(bytes: self.bytes) } } diff --git a/Sources/QueryEngine/FileSystem/VirtualFileSystem.swift b/Sources/QueryEngine/FileSystem/VirtualFileSystem.swift index 581e0776aa9..db8af267bbf 100644 --- a/Sources/QueryEngine/FileSystem/VirtualFileSystem.swift +++ b/Sources/QueryEngine/FileSystem/VirtualFileSystem.swift @@ -13,7 +13,7 @@ import struct SystemPackage.FilePath actor VirtualFileSystem: AsyncFileSystem { - public static let defaultChunkSize = 512 * 1024 + package static let defaultChunkSize = 512 * 1024 let readChunkSize: Int @@ -32,14 +32,20 @@ actor VirtualFileSystem: AsyncFileSystem { self.readChunkSize = readChunkSize } - func withOpenReadableFile(_ path: FilePath, _ body: (OpenReadableFile) async throws -> T) async throws -> T { + func withOpenReadableFile( + _ path: FilePath, + _ body: (OpenReadableFile) async throws -> T + ) async throws -> T { guard let bytes = storage.content[path] else { throw FileSystemError.fileDoesNotExist(path) } return try await body(.init(readChunkSize: self.readChunkSize, fileHandle: .virtual(bytes))) } - func withOpenWritableFile(_ path: FilePath, _ body: (OpenWritableFile) async throws -> T) async throws -> T { + func withOpenWritableFile( + _ path: FilePath, + _ body: (OpenWritableFile) async throws -> T + ) async throws -> T { try await body(.init(fileHandle: .virtual(self.storage, path))) } } diff --git a/Sources/QueryEngine/Query.swift b/Sources/QueryEngine/Query.swift index 536db5d22a6..e04fc7d6879 100644 --- a/Sources/QueryEngine/Query.swift +++ b/Sources/QueryEngine/Query.swift @@ -12,12 +12,255 @@ import Crypto import struct SystemPackage.FilePath -public protocol Query: Encodable { +package protocol Query: CacheKey, Sendable { func run(engine: QueryEngine) async throws -> FilePath } -extension Query { - func hash(with hashFunction: inout some HashFunction) { - fatalError("\(#function) not implemented") +// SwiftPM has to be built with Swift 5.8 on CI and also needs to support CMake for bootstrapping on Windows. +// This means we can't implement persistable hashing with macros (unavailable in Swift 5.8 and additional effort to +// set up with CMake when Swift 5.9 is available for all CI jobs) and have to stick to `Encodable` for now. +final class HashEncoder: Encoder { + enum Error: Swift.Error { + case noCacheKeyConformance(Encodable.Type) + } + + var codingPath: [any CodingKey] + + var userInfo: [CodingUserInfoKey : Any] + + func container(keyedBy type: Key.Type) -> KeyedEncodingContainer where Key : CodingKey { + String(reflecting: Key.self).hash(with: &self.hashFunction) + return .init(KeyedContainer(encoder: self)) + } + + func unkeyedContainer() -> any UnkeyedEncodingContainer { + self + } + + func singleValueContainer() -> any SingleValueEncodingContainer { + self + } + + init() { + self.hashFunction = Hash() + self.codingPath = [] + self.userInfo = [:] + } + + fileprivate var hashFunction = Hash() + + func finalize() -> Hash.Digest { + hashFunction.finalize() + } +} + +extension HashEncoder: SingleValueEncodingContainer { + func encodeNil() throws { + // FIXME: this doesn't encode the name of the underlying optional type, + // but `Encoder` protocol is limited and can't provide this for us. + var str = "nil" + str.withUTF8 { + self.hashFunction.update(data: $0) + } + } + + func encode(_ value: Bool) throws { + value.hash(with: &self.hashFunction) + } + + func encode(_ value: String) throws { + value.hash(with: &self.hashFunction) + } + + func encode(_ value: Double) throws { + value.hash(with: &self.hashFunction) + } + + func encode(_ value: Float) throws { + value.hash(with: &self.hashFunction) + } + + func encode(_ value: Int) throws { + value.hash(with: &self.hashFunction) + } + + func encode(_ value: Int8) throws { + value.hash(with: &self.hashFunction) + } + + func encode(_ value: Int16) throws { + value.hash(with: &self.hashFunction) + } + + func encode(_ value: Int32) throws { + value.hash(with: &self.hashFunction) + } + + func encode(_ value: Int64) throws { + value.hash(with: &self.hashFunction) + } + + func encode(_ value: UInt) throws { + value.hash(with: &self.hashFunction) + } + + func encode(_ value: UInt8) throws { + value.hash(with: &self.hashFunction) + } + + func encode(_ value: UInt16) throws { + value.hash(with: &self.hashFunction) + } + + func encode(_ value: UInt32) throws { + value.hash(with: &self.hashFunction) + } + + func encode(_ value: UInt64) throws { + value.hash(with: &self.hashFunction) + } + + func encode(_ value: T) throws where T : Encodable { + guard value is CacheKey else { + throw Error.noCacheKeyConformance(T.self) + } + + try value.encode(to: self) + } +} + +extension HashEncoder: UnkeyedEncodingContainer { + var count: Int { + 0 + } + + func nestedContainer(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer where NestedKey : CodingKey { + KeyedEncodingContainer(KeyedContainer(encoder: self)) + } + + func nestedUnkeyedContainer() -> any UnkeyedEncodingContainer { + self + } + + func superEncoder() -> any Encoder { + fatalError() + } +} + +extension HashEncoder { + struct KeyedContainer: KeyedEncodingContainerProtocol { + var encoder: HashEncoder + var codingPath: [any CodingKey] { self.encoder.codingPath } + + mutating func encodeNil(forKey key: K) throws { + // FIXME: this doesn't encode the name of the underlying optional type, + // but `Encoder` protocol is limited and can't provide this for us. + var str = "nil" + str.withUTF8 { + self.encoder.hashFunction.update(data: $0) + } + } + + mutating func encode(_ value: Bool, forKey key: K) throws { + key.stringValue.hash(with: &self.encoder.hashFunction) + value.hash(with: &self.encoder.hashFunction) + } + + mutating func encode(_ value: String, forKey key: K) throws { + key.stringValue.hash(with: &self.encoder.hashFunction) + value.hash(with: &self.encoder.hashFunction) + } + + mutating func encode(_ value: Double, forKey key: K) throws { + key.stringValue.hash(with: &self.encoder.hashFunction) + value.hash(with: &self.encoder.hashFunction) + } + + mutating func encode(_ value: Float, forKey key: K) throws { + key.stringValue.hash(with: &self.encoder.hashFunction) + value.hash(with: &self.encoder.hashFunction) + } + + mutating func encode(_ value: Int, forKey key: K) throws { + key.stringValue.hash(with: &self.encoder.hashFunction) + value.hash(with: &self.encoder.hashFunction) + } + + mutating func encode(_ value: Int8, forKey key: K) throws { + key.stringValue.hash(with: &self.encoder.hashFunction) + value.hash(with: &self.encoder.hashFunction) + } + + mutating func encode(_ value: Int16, forKey key: K) throws { + key.stringValue.hash(with: &self.encoder.hashFunction) + value.hash(with: &self.encoder.hashFunction) + } + + mutating func encode(_ value: Int32, forKey key: K) throws { + key.stringValue.hash(with: &self.encoder.hashFunction) + value.hash(with: &self.encoder.hashFunction) + } + + mutating func encode(_ value: Int64, forKey key: K) throws { + key.stringValue.hash(with: &self.encoder.hashFunction) + value.hash(with: &self.encoder.hashFunction) + } + + mutating func encode(_ value: UInt, forKey key: K) throws { + key.stringValue.hash(with: &self.encoder.hashFunction) + value.hash(with: &self.encoder.hashFunction) + } + + mutating func encode(_ value: UInt8, forKey key: K) throws { + key.stringValue.hash(with: &self.encoder.hashFunction) + value.hash(with: &self.encoder.hashFunction) + } + + mutating func encode(_ value: UInt16, forKey key: K) throws { + key.stringValue.hash(with: &self.encoder.hashFunction) + value.hash(with: &self.encoder.hashFunction) + } + + mutating func encode(_ value: UInt32, forKey key: K) throws { + key.stringValue.hash(with: &self.encoder.hashFunction) + value.hash(with: &self.encoder.hashFunction) + } + + mutating func encode(_ value: UInt64, forKey key: K) throws { + key.stringValue.hash(with: &self.encoder.hashFunction) + value.hash(with: &self.encoder.hashFunction) + } + + mutating func encode(_ value: T, forKey key: K) throws where T : Encodable { + guard value is CacheKey else { + throw Error.noCacheKeyConformance(T.self) + } + + key.stringValue.hash(with: &self.encoder.hashFunction) + try value.encode(to: self.encoder) + } + + mutating func nestedContainer( + keyedBy keyType: NestedKey.Type, + forKey key: K + ) -> KeyedEncodingContainer where NestedKey : CodingKey { + key.stringValue.hash(with: &self.encoder.hashFunction) + return self.encoder.nestedContainer(keyedBy: keyType) + } + + mutating func nestedUnkeyedContainer(forKey key: K) -> any UnkeyedEncodingContainer { + key.stringValue.hash(with: &self.encoder.hashFunction) + return self.encoder + } + + mutating func superEncoder() -> any Encoder { + fatalError() + } + + mutating func superEncoder(forKey key: K) -> any Encoder { + fatalError() + } + + typealias Key = K } } diff --git a/Sources/QueryEngine/QueryEngine.swift b/Sources/QueryEngine/QueryEngine.swift index a6536ac9892..dfeb3997bd2 100644 --- a/Sources/QueryEngine/QueryEngine.swift +++ b/Sources/QueryEngine/QueryEngine.swift @@ -12,8 +12,9 @@ import Basics import Crypto +@preconcurrency import SystemPackage -public func withQueryEngine( +package func withQueryEngine( _ fileSystem: any AsyncFileSystem, _ observabilityScope: ObservabilityScope, cacheLocation: SQLite.Location, @@ -34,13 +35,13 @@ public func withQueryEngine( /// Cacheable computations engine. Currently the engine makes an assumption that computations produce same results for /// the same query values and write results to a single file path. -public actor QueryEngine { +package actor QueryEngine { private(set) var cacheHits = 0 private(set) var cacheMisses = 0 - public let fileSystem: any AsyncFileSystem - public let httpClient = HTTPClient() - public let observabilityScope: ObservabilityScope + package let fileSystem: any AsyncFileSystem + package let httpClient = HTTPClient() + package let observabilityScope: ObservabilityScope private let resultsCache: SQLiteBackedCache private var isShutDown = false @@ -60,7 +61,7 @@ public actor QueryEngine { self.resultsCache = SQLiteBackedCache(tableName: "cache_table", location: cacheLocation) } - public func shutDown() async throws { + package func shutDown() async throws { precondition(!self.isShutDown, "`QueryEngine/shutDown` should be called only once") try self.resultsCache.close() @@ -78,18 +79,19 @@ public actor QueryEngine { /// Executes a given query if no cached result of it is available. Otherwise fetches the result from engine's cache. /// - Parameter query: A query value to execute. /// - Returns: A file path to query's result recorded in a file. - public subscript(_ query: some Query) -> FileCacheRecord { + package subscript(_ query: some Query) -> FileCacheRecord { get async throws { - var hashFunction = SHA512() - query.hash(with: &hashFunction) - let key = hashFunction.finalize() + let hashEncoder = HashEncoder() + try query.encode(to: hashEncoder) + let key = hashEncoder.finalize() if let fileRecord = try resultsCache.get(blobKey: key) { - hashFunction = SHA512() - try await self.fileSystem.withOpenReadableFile(fileRecord.path) { + + let fileHash = try await self.fileSystem.withOpenReadableFile(fileRecord.path) { + var hashFunction = SHA512() try await $0.hash(with: &hashFunction) + return hashFunction.finalize().description } - let fileHash = hashFunction.finalize().description if fileHash == fileRecord.hash { self.cacheHits += 1 @@ -99,13 +101,13 @@ public actor QueryEngine { self.cacheMisses += 1 let resultPath = try await query.run(engine: self) - hashFunction = SHA512() - try await self.fileSystem.withOpenReadableFile(resultPath) { + let resultHash = try await self.fileSystem.withOpenReadableFile(resultPath) { + var hashFunction = SHA512() try await $0.hash(with: &hashFunction) + return hashFunction.finalize().description } - let resultHash = hashFunction.finalize() - let result = FileCacheRecord(path: resultPath, hash: resultHash.description) + let result = FileCacheRecord(path: resultPath, hash: resultHash) // FIXME: update `SQLiteBackedCache` to store `resultHash` directly instead of relying on string conversions try self.resultsCache.put(blobKey: key, value: result) diff --git a/Tests/QueryEngineTests/QueryEngineTests.swift b/Tests/QueryEngineTests/QueryEngineTests.swift new file mode 100644 index 00000000000..39c92af40d2 --- /dev/null +++ b/Tests/QueryEngineTests/QueryEngineTests.swift @@ -0,0 +1,153 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import class Basics.ObservabilitySystem +import struct Foundation.Data +@testable import QueryEngine +import struct SystemPackage.FilePath +import SPMTestSupport +import XCTest + +private let encoder = JSONEncoder() +private let decoder = JSONDecoder() + +private extension AsyncFileSystem { + func read(_ path: FilePath, bufferLimit: Int = 10 * 1024 * 1024, as: V.Type) async throws -> V { + let data = try await self.withOpenReadableFile(path) { + var data = Data() + for try await chunk in try await $0.read() { + data.append(contentsOf: chunk) + + guard data.count < bufferLimit else { + throw FileSystemError.bufferLimitExceeded(path) + } + } + return data + } + + return try decoder.decode(V.self, from: data) + } + + func write(_ path: FilePath, _ value: some Encodable) async throws { + let data = try encoder.encode(value) + try await self.withOpenWritableFile(path) { fileHandle in + try await fileHandle.write(data) + } + } +} + +private struct Const: Query { + let x: Int + + func run(engine: QueryEngine) async throws -> FilePath { + let resultPath = FilePath("/Const-\(x)") + try await engine.fileSystem.write(resultPath, self.x) + return resultPath + } +} + +private struct MultiplyByTwo: Query { + let x: Int + + func run(engine: QueryEngine) async throws -> FilePath { + let constPath = try await engine[Const(x: self.x)].path + let constResult = try await engine.fileSystem.read(constPath, as: Int.self) + + let resultPath = FilePath("/MultiplyByTwo-\(constResult)") + try await engine.fileSystem.write(resultPath, constResult * 2) + return resultPath + } +} + +private struct AddThirty: Query { + let x: Int + + func run(engine: QueryEngine) async throws -> FilePath { + let constPath = try await engine[Const(x: self.x)].path + let constResult = try await engine.fileSystem.read(constPath, as: Int.self) + + let resultPath = FilePath("/AddThirty-\(constResult)") + try await engine.fileSystem.write(resultPath, constResult + 30) + return resultPath + } +} + +private struct Expression: Query { + let x: Int + let y: Int + + func run(engine: QueryEngine) async throws -> FilePath { + let multiplyPath = try await engine[MultiplyByTwo(x: self.x)].path + let addThirtyPath = try await engine[AddThirty(x: self.y)].path + + let multiplyResult = try await engine.fileSystem.read(multiplyPath, as: Int.self) + let addThirtyResult = try await engine.fileSystem.read(addThirtyPath, as: Int.self) + + let resultPath = FilePath("/Expression-\(multiplyResult)-\(addThirtyResult)") + try await engine.fileSystem.write(resultPath, multiplyResult + addThirtyResult) + return resultPath + } +} + +final class QueryEngineTests: XCTestCase { + func testSimpleCaching() async throws { + let observabilitySystem = ObservabilitySystem.makeForTesting() + let engine = QueryEngine( + VirtualFileSystem(), + observabilitySystem.topScope, + cacheLocation: .memory + ) + + var resultPath = try await engine[Expression(x: 1, y: 2)].path + var result = try await engine.fileSystem.read(resultPath, as: Int.self) + + XCTAssertEqual(result, 34) + + var cacheMisses = await engine.cacheMisses + XCTAssertEqual(cacheMisses, 5) + + var cacheHits = await engine.cacheHits + XCTAssertEqual(cacheHits, 0) + + resultPath = try await engine[Expression(x: 1, y: 2)].path + result = try await engine.fileSystem.read(resultPath, as: Int.self) + XCTAssertEqual(result, 34) + + cacheMisses = await engine.cacheMisses + XCTAssertEqual(cacheMisses, 5) + + cacheHits = await engine.cacheHits + XCTAssertEqual(cacheHits, 1) + + resultPath = try await engine[Expression(x: 2, y: 1)].path + result = try await engine.fileSystem.read(resultPath, as: Int.self) + XCTAssertEqual(result, 35) + + cacheMisses = await engine.cacheMisses + XCTAssertEqual(cacheMisses, 8) + + cacheHits = await engine.cacheHits + XCTAssertEqual(cacheHits, 3) + + resultPath = try await engine[Expression(x: 2, y: 1)].path + result = try await engine.fileSystem.read(resultPath, as: Int.self) + XCTAssertEqual(result, 35) + + cacheMisses = await engine.cacheMisses + XCTAssertEqual(cacheMisses, 8) + + cacheHits = await engine.cacheHits + XCTAssertEqual(cacheHits, 4) + + try await engine.shutDown() + } +} From 8df1d144cf1c17d21ad8306e0ed5efd0aa640d6a Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 3 Apr 2024 13:11:37 -0700 Subject: [PATCH 078/159] [PackageDescription] Make `SwiftVersion` convertible to string --- .../LanguageStandardSettings.swift | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Sources/PackageDescription/LanguageStandardSettings.swift b/Sources/PackageDescription/LanguageStandardSettings.swift index 23bf688277a..e7b61c2cdc6 100644 --- a/Sources/PackageDescription/LanguageStandardSettings.swift +++ b/Sources/PackageDescription/LanguageStandardSettings.swift @@ -172,3 +172,15 @@ public enum SwiftVersion { /// The value is passed as-is to the Swift compiler's `-swift-version` flag. case version(String) } + +extension SwiftVersion: CustomStringConvertible { + public var description: String { + switch self { + case .v3: "3" + case .v4: "4" + case .v4_2: "4.2" + case .v5: "5" + case .version(let version): version + } + } +} From 7d476694db682dea2e61280f27d2aeb979a94c26 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 3 Apr 2024 15:44:33 -0700 Subject: [PATCH 079/159] Implement per target swift version selection Swift Forums pitch thread - https://forums.swift.org/t/pitch-swiftpm-swift-language-version-per-target/71067 Add a new Swift target setting API, similar to `enable{Upcoming, Experimental}Feature`, to specify a Swift language version that should be used to build the target, if such version is not specified, fallback to the current language version determination logic. Resolves: rdar://125732014 --- .../SwiftTargetBuildDescription.swift | 6 +- .../PackageDescription/BuildSettings.swift | 17 +++++ .../PackageLoading/ManifestJSONParser.swift | 15 +++++ Sources/PackageLoading/PackageBuilder.swift | 11 ++++ .../TargetBuildSettingDescription.swift | 6 +- .../ManifestSourceGeneration.swift | 8 +++ Tests/BuildTests/BuildPlanTests.swift | 48 +++++++++----- .../CrossCompilationBuildPlanTests.swift | 4 +- .../PackageBuilderTests.swift | 63 +++++++++++++++++++ .../ManifestSourceGenerationTests.swift | 34 ++++++++++ 10 files changed, 192 insertions(+), 20 deletions(-) diff --git a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift index cbb9a1d446b..7a7572aa1f0 100644 --- a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift @@ -477,7 +477,6 @@ package final class SwiftTargetBuildDescription { package func compileArguments() throws -> [String] { var args = [String]() args += try self.buildParameters.targetTripleArgs(for: self.target) - args += ["-swift-version", self.swiftVersion.rawValue] // pass `-v` during verbose builds. if self.buildParameters.outputParameters.isVerbose { @@ -579,6 +578,11 @@ package final class SwiftTargetBuildDescription { // Add arguments from declared build settings. args += try self.buildSettingsFlags() + // Fallback to package wide setting if there is no target specific version. + if args.firstIndex(of: "-swift-version") == nil { + args += ["-swift-version", self.swiftVersion.rawValue] + } + // Add the output for the `.swiftinterface`, if requested or if library evolution has been enabled some other // way. if self.buildParameters.driverParameters.enableParseableModuleInterfaces || args.contains("-enable-library-evolution") { diff --git a/Sources/PackageDescription/BuildSettings.swift b/Sources/PackageDescription/BuildSettings.swift index d28b298ec64..239f150d53f 100644 --- a/Sources/PackageDescription/BuildSettings.swift +++ b/Sources/PackageDescription/BuildSettings.swift @@ -387,6 +387,23 @@ public struct SwiftSetting { return SwiftSetting( name: "interoperabilityMode", value: [mode.rawValue], condition: condition) } + + /// Defines a `-swift-version` to pass to the + /// corresponding build tool. + /// + /// - Since: First available in PackageDescription 6.0. + /// + /// - Parameters: + /// - version: The Swift language version to use. + /// - condition: A condition that restricts the application of the build setting. + @available(_PackageDescription, introduced: 6.0) + public static func swiftLanguageVersion( + _ version: SwiftVersion, + _ condition: BuildSettingCondition? = nil + ) -> SwiftSetting { + return SwiftSetting( + name: "swiftLanguageVersion", value: [.init(describing: version)], condition: condition) + } } /// A linker build setting. diff --git a/Sources/PackageLoading/ManifestJSONParser.swift b/Sources/PackageLoading/ManifestJSONParser.swift index ad1322668ea..e989b64e692 100644 --- a/Sources/PackageLoading/ManifestJSONParser.swift +++ b/Sources/PackageLoading/ManifestJSONParser.swift @@ -533,6 +533,21 @@ extension TargetBuildSettingDescription.Kind { return .enableExperimentalFeature(value) case "unsafeFlags": return .unsafeFlags(values) + + case "swiftLanguageVersion": + guard let rawVersion = values.first else { + throw InternalError("invalid (empty) build settings value") + } + + if values.count > 1 { + throw InternalError("invalid build settings value") + } + + guard let version = SwiftLanguageVersion(string: rawVersion) else { + throw InternalError("unknown swift language version: \(rawVersion)") + } + + return .swiftLanguageVersion(version) default: throw InternalError("invalid build setting \(name)") } diff --git a/Sources/PackageLoading/PackageBuilder.swift b/Sources/PackageLoading/PackageBuilder.swift index a1c13034e67..c81ccb1fadd 100644 --- a/Sources/PackageLoading/PackageBuilder.swift +++ b/Sources/PackageLoading/PackageBuilder.swift @@ -1140,6 +1140,17 @@ public final class PackageBuilder { } values = ["-enable-experimental-feature", value] + + case .swiftLanguageVersion(let version): + switch setting.tool { + case .c, .cxx, .linker: + throw InternalError("only Swift supports swift language version") + + case .swift: + decl = .OTHER_SWIFT_FLAGS + } + + values = ["-swift-version", version.rawValue] } // Create an assignment for this setting. diff --git a/Sources/PackageModel/Manifest/TargetBuildSettingDescription.swift b/Sources/PackageModel/Manifest/TargetBuildSettingDescription.swift index a0eb9be2575..e535c1c9b16 100644 --- a/Sources/PackageModel/Manifest/TargetBuildSettingDescription.swift +++ b/Sources/PackageModel/Manifest/TargetBuildSettingDescription.swift @@ -12,7 +12,6 @@ /// A namespace for target-specific build settings. public enum TargetBuildSettingDescription { - /// The tool for which a build setting is declared. public enum Tool: String, Codable, Hashable, CaseIterable, Sendable { case c @@ -40,12 +39,15 @@ public enum TargetBuildSettingDescription { case unsafeFlags([String]) + case swiftLanguageVersion(SwiftLanguageVersion) + public var isUnsafeFlags: Bool { switch self { case .unsafeFlags(let flags): // If `.unsafeFlags` is used, but doesn't specify any flags, we treat it the same way as not specifying it. return !flags.isEmpty - case .headerSearchPath, .define, .linkedLibrary, .linkedFramework, .interoperabilityMode, .enableUpcomingFeature, .enableExperimentalFeature: + case .headerSearchPath, .define, .linkedLibrary, .linkedFramework, .interoperabilityMode, + .enableUpcomingFeature, .enableExperimentalFeature, .swiftLanguageVersion: return false } } diff --git a/Sources/PackageModel/ManifestSourceGeneration.swift b/Sources/PackageModel/ManifestSourceGeneration.swift index 8d630deb9cc..f2a31e932fd 100644 --- a/Sources/PackageModel/ManifestSourceGeneration.swift +++ b/Sources/PackageModel/ManifestSourceGeneration.swift @@ -525,6 +525,12 @@ fileprivate extension SourceCodeFragment { params.append(SourceCodeFragment(from: condition)) } self.init(enum: setting.kind.name, subnodes: params) + case .swiftLanguageVersion(let version): + params.append(SourceCodeFragment(from: version)) + if let condition = setting.condition { + params.append(SourceCodeFragment(from: condition)) + } + self.init(enum: setting.kind.name, subnodes: params) } } } @@ -677,6 +683,8 @@ extension TargetBuildSettingDescription.Kind { return "enableUpcomingFeature" case .enableExperimentalFeature: return "enableExperimentalFeature" + case .swiftLanguageVersion: + return "swiftLanguageVersion" } } } diff --git a/Tests/BuildTests/BuildPlanTests.swift b/Tests/BuildTests/BuildPlanTests.swift index a68d4429e4d..0fbbab8b6c9 100644 --- a/Tests/BuildTests/BuildPlanTests.swift +++ b/Tests/BuildTests/BuildPlanTests.swift @@ -769,7 +769,6 @@ final class BuildPlanTests: XCTestCase { XCTAssertMatch( exe, [ - "-swift-version", "4", "-enable-batch-mode", "-Onone", "-enable-testing", @@ -778,6 +777,7 @@ final class BuildPlanTests: XCTestCase { "-DDEBUG", "-module-cache-path", "\(buildPath.appending(components: "ModuleCache"))", .anySequence, + "-swift-version", "4", "-g", .anySequence, ] @@ -787,7 +787,6 @@ final class BuildPlanTests: XCTestCase { XCTAssertMatch( lib, [ - "-swift-version", "4", "-enable-batch-mode", "-Onone", "-enable-testing", @@ -796,6 +795,7 @@ final class BuildPlanTests: XCTestCase { "-DDEBUG", "-module-cache-path", "\(buildPath.appending(components: "ModuleCache"))", .anySequence, + "-swift-version", "4", "-g", .anySequence, ] @@ -1205,12 +1205,12 @@ final class BuildPlanTests: XCTestCase { XCTAssertMatch( exe, [ - "-swift-version", "4", "-O", .equal(self.j), "-DSWIFT_PACKAGE", "-module-cache-path", "\(buildPath.appending(components: "ModuleCache"))", .anySequence, + "-swift-version", "4", "-g", ] ) @@ -1296,12 +1296,12 @@ final class BuildPlanTests: XCTestCase { XCTAssertMatch( exe, [ - "-swift-version", "4", "-O", .equal(self.j), "-DSWIFT_PACKAGE", "-module-cache-path", "\(buildPath.appending(components: "ModuleCache"))", .anySequence, + "-swift-version", "4", "-g", ] ) @@ -1795,7 +1795,6 @@ final class BuildPlanTests: XCTestCase { exe, [ .anySequence, - "-swift-version", "4", "-enable-batch-mode", "-Onone", "-enable-testing", @@ -1807,6 +1806,7 @@ final class BuildPlanTests: XCTestCase { "-Xcc", "-I", "-Xcc", "\(Pkg.appending(components: "Sources", "lib", "include"))", "-module-cache-path", "\(buildPath.appending(components: "ModuleCache"))", .anySequence, + "-swift-version", "4", "-g", .anySequence, ] @@ -2017,7 +2017,6 @@ final class BuildPlanTests: XCTestCase { foo, [ .anySequence, - "-swift-version", "4", "-enable-batch-mode", "-Onone", "-enable-testing", @@ -2026,6 +2025,7 @@ final class BuildPlanTests: XCTestCase { "-DDEBUG", "-module-cache-path", "\(buildPath.appending(components: "ModuleCache"))", .anySequence, + "-swift-version", "4", "-g", .anySequence, ] @@ -2036,7 +2036,6 @@ final class BuildPlanTests: XCTestCase { fooTests, [ .anySequence, - "-swift-version", "4", "-enable-batch-mode", "-Onone", "-enable-testing", @@ -2045,6 +2044,7 @@ final class BuildPlanTests: XCTestCase { "-DDEBUG", "-module-cache-path", "\(buildPath.appending(components: "ModuleCache"))", .anySequence, + "-swift-version", "4", "-g", .anySequence, ] @@ -2149,12 +2149,12 @@ final class BuildPlanTests: XCTestCase { exe, [ .anySequence, - "-swift-version", "4", "-O", .equal(self.j), "-DSWIFT_PACKAGE", "-module-cache-path", "\(buildPath.appending(components: "ModuleCache"))", .anySequence, + "-swift-version", "4", "-g", .anySequence, ] @@ -2510,7 +2510,6 @@ final class BuildPlanTests: XCTestCase { XCTAssertMatch( try result.target(for: "exe").swiftTarget().compileArguments(), [ - "-swift-version", "4", "-enable-batch-mode", "-Onone", "-enable-testing", @@ -2520,6 +2519,7 @@ final class BuildPlanTests: XCTestCase { "-Xcc", "-fmodule-map-file=\(Clibgit.appending(components: "module.modulemap"))", "-module-cache-path", "\(buildPath.appending(components: "ModuleCache"))", .anySequence, + "-swift-version", "4", "-g", .anySequence, ] @@ -2810,7 +2810,6 @@ final class BuildPlanTests: XCTestCase { XCTAssertMatch( exe, [ - "-swift-version", "4", "-enable-batch-mode", "-Onone", "-enable-testing", @@ -2819,6 +2818,7 @@ final class BuildPlanTests: XCTestCase { "-DDEBUG", "-module-cache-path", "\(buildPath.appending(components: "ModuleCache"))", .anySequence, + "-swift-version", "4", "-g", .anySequence, ] @@ -2828,7 +2828,6 @@ final class BuildPlanTests: XCTestCase { XCTAssertMatch( lib, [ - "-swift-version", "4", "-enable-batch-mode", "-Onone", "-enable-testing", @@ -2837,6 +2836,7 @@ final class BuildPlanTests: XCTestCase { "-DDEBUG", "-module-cache-path", "\(buildPath.appending(components: "ModuleCache"))", .anySequence, + "-swift-version", "4", "-g", .anySequence, ] @@ -3447,7 +3447,6 @@ final class BuildPlanTests: XCTestCase { let exe = try result.target(for: "exe").swiftTarget().compileArguments() XCTAssertMatch(exe, [ - "-swift-version", "4", "-enable-batch-mode", "-Onone", "-enable-testing", @@ -3457,6 +3456,7 @@ final class BuildPlanTests: XCTestCase { "-Xcc", "-I", "-Xcc", "\(Pkg.appending(components: "Sources", "lib", "include"))", "-module-cache-path", "\(buildPath.appending(components: "ModuleCache"))", .anySequence, + "-swift-version", "4", "-g", "-use-ld=lld", "-Xcc", "-gdwarf", @@ -3954,6 +3954,16 @@ final class BuildPlanTests: XCTestCase { kind: .interoperabilityMode(.Cxx), condition: .init(platformNames: ["macos"]) ), + .init( + tool: .swift, + kind: .swiftLanguageVersion(.v4), + condition: .init(platformNames: ["macos"]) + ), + .init( + tool: .swift, + kind: .swiftLanguageVersion(.v5), + condition: .init(platformNames: ["linux"]) + ), .init(tool: .linker, kind: .linkedLibrary("sqlite3")), .init( tool: .linker, @@ -3984,6 +3994,8 @@ final class BuildPlanTests: XCTestCase { name: "t1", settings: [ .init(tool: .swift, kind: .define("DEP")), + .init(tool: .swift, kind: .swiftLanguageVersion(.v4), condition: .init(platformNames: ["linux"])), + .init(tool: .swift, kind: .swiftLanguageVersion(.v5), condition: .init(platformNames: ["macos"])), .init(tool: .linker, kind: .linkedLibrary("libz")), ] ), @@ -4018,6 +4030,7 @@ final class BuildPlanTests: XCTestCase { let dep = try result.target(for: "t1").swiftTarget().compileArguments() XCTAssertMatch(dep, [.anySequence, "-DDEP", .anySequence]) + XCTAssertMatch(dep, [.anySequence, "-swift-version", "4", .anySequence]) let cbar = try result.target(for: "cbar").clangTarget().basicArguments(isCXX: false) XCTAssertMatch( @@ -4048,6 +4061,7 @@ final class BuildPlanTests: XCTestCase { "-cxx-interoperability-mode=default", "-Xcc", "-std=c++17", "-enable-upcoming-feature", "BestFeature", + "-swift-version", "5", "-g", "-Xcc", "-g", "-Xcc", "-fno-omit-frame-pointer", @@ -4056,7 +4070,7 @@ final class BuildPlanTests: XCTestCase { ) let exe = try result.target(for: "exe").swiftTarget().compileArguments() - XCTAssertMatch(exe, [.anySequence, "-DFOO", "-g", "-Xcc", "-g", "-Xcc", "-fno-omit-frame-pointer", .end]) + XCTAssertMatch(exe, [.anySequence, "-DFOO", "-swift-version", "5", "-g", "-Xcc", "-g", "-Xcc", "-fno-omit-frame-pointer", .end]) let linkExe = try result.buildProduct(for: "exe").linkArguments() XCTAssertMatch(linkExe, [.anySequence, "-lsqlite3", "-llibz", "-Ilfoo", "-L", "lbar", "-g", .end]) @@ -4113,6 +4127,7 @@ final class BuildPlanTests: XCTestCase { "-Xcc", "-std=c++17", "-enable-upcoming-feature", "BestFeature", + "-swift-version", "5", "-g", "-Xcc", "-g", "-Xcc", "-fomit-frame-pointer", @@ -4121,7 +4136,7 @@ final class BuildPlanTests: XCTestCase { ) let exe = try result.target(for: "exe").swiftTarget().compileArguments() - XCTAssertMatch(exe, [.anySequence, "-DFOO", "-g", "-Xcc", "-g", "-Xcc", "-fomit-frame-pointer", .end]) + XCTAssertMatch(exe, [.anySequence, "-DFOO", "-swift-version", "5", "-g", "-Xcc", "-g", "-Xcc", "-fomit-frame-pointer", .end]) } // omit frame pointers explicitly set to false @@ -4169,6 +4184,7 @@ final class BuildPlanTests: XCTestCase { "-Xcc", "-std=c++17", "-enable-upcoming-feature", "BestFeature", + "-swift-version", "5", "-g", "-Xcc", "-g", "-Xcc", "-fno-omit-frame-pointer", @@ -4177,7 +4193,7 @@ final class BuildPlanTests: XCTestCase { ) let exe = try result.target(for: "exe").swiftTarget().compileArguments() - XCTAssertMatch(exe, [.anySequence, "-DFOO", "-g", "-Xcc", "-g", "-Xcc", "-fno-omit-frame-pointer", .end]) + XCTAssertMatch(exe, [.anySequence, "-DFOO", "-swift-version", "5", "-g", "-Xcc", "-g", "-Xcc", "-fno-omit-frame-pointer", .end]) } do { @@ -4212,6 +4228,7 @@ final class BuildPlanTests: XCTestCase { "-Xcc", "-std=c++17", "-enable-upcoming-feature", "BestFeature", "-enable-upcoming-feature", "WorstFeature", + "-swift-version", "5", "-g", "-Xcc", "-g", .end, @@ -4226,6 +4243,7 @@ final class BuildPlanTests: XCTestCase { "-DFOO", "-cxx-interoperability-mode=default", "-Xcc", "-std=c++17", + "-swift-version", "4", "-g", "-Xcc", "-g", .end, diff --git a/Tests/BuildTests/CrossCompilationBuildPlanTests.swift b/Tests/BuildTests/CrossCompilationBuildPlanTests.swift index 028ea94f3cd..65ac52d9076 100644 --- a/Tests/BuildTests/CrossCompilationBuildPlanTests.swift +++ b/Tests/BuildTests/CrossCompilationBuildPlanTests.swift @@ -165,12 +165,12 @@ final class CrossCompilationBuildPlanTests: XCTestCase { XCTAssertMatch( exe, [ - "-swift-version", "4", "-enable-batch-mode", "-Onone", "-enable-testing", + "-enable-batch-mode", "-Onone", "-enable-testing", "-j3", "-DSWIFT_PACKAGE", "-DDEBUG", "-Xcc", "-fmodule-map-file=\(buildPath.appending(components: "lib.build", "module.modulemap"))", "-Xcc", "-I", "-Xcc", "\(pkgPath.appending(components: "Sources", "lib", "include"))", "-module-cache-path", "\(buildPath.appending(components: "ModuleCache"))", .anySequence, - "-g", .anySequence, + "-swift-version", "4", "-g", .anySequence, ] ) diff --git a/Tests/PackageLoadingTests/PackageBuilderTests.swift b/Tests/PackageLoadingTests/PackageBuilderTests.swift index 587439049b1..4b24f5779bf 100644 --- a/Tests/PackageLoadingTests/PackageBuilderTests.swift +++ b/Tests/PackageLoadingTests/PackageBuilderTests.swift @@ -3022,6 +3022,69 @@ final class PackageBuilderTests: XCTestCase { } } } + + func testSwiftLanguageVesionPerTarget() throws { + let fs = InMemoryFileSystem(emptyFiles: + "/Sources/foo/foo.swift", + "/Sources/bar/bar.swift" + ) + + let manifest = Manifest.createRootManifest( + displayName: "pkg", + toolsVersion: .v5, + targets: [ + try TargetDescription( + name: "foo", + settings: [ + .init(tool: .swift, kind: .swiftLanguageVersion(.v5)) + ] + ), + try TargetDescription( + name: "bar", + settings: [ + .init(tool: .swift, kind: .swiftLanguageVersion(.v3), condition: .init(platformNames: ["linux"])), + .init(tool: .swift, kind: .swiftLanguageVersion(.v4), condition: .init(platformNames: ["macos"], config: "debug")) + ] + ), + ] + ) + + PackageBuilderTester(manifest, in: fs) { package, _ in + package.checkModule("foo") { package in + let macosDebugScope = BuildSettings.Scope( + package.target.buildSettings, + environment: BuildEnvironment(platform: .macOS, configuration: .debug) + ) + XCTAssertEqual(macosDebugScope.evaluate(.OTHER_SWIFT_FLAGS), ["-swift-version", "5"]) + + let macosReleaseScope = BuildSettings.Scope( + package.target.buildSettings, + environment: BuildEnvironment(platform: .macOS, configuration: .release) + ) + XCTAssertEqual(macosReleaseScope.evaluate(.OTHER_SWIFT_FLAGS), ["-swift-version", "5"]) + } + + package.checkModule("bar") { package in + let linuxDebugScope = BuildSettings.Scope( + package.target.buildSettings, + environment: BuildEnvironment(platform: .linux, configuration: .debug) + ) + XCTAssertEqual(linuxDebugScope.evaluate(.OTHER_SWIFT_FLAGS), ["-swift-version", "3"]) + + let macosDebugScope = BuildSettings.Scope( + package.target.buildSettings, + environment: BuildEnvironment(platform: .macOS, configuration: .debug) + ) + XCTAssertEqual(macosDebugScope.evaluate(.OTHER_SWIFT_FLAGS), ["-swift-version", "4"]) + + let macosReleaseScope = BuildSettings.Scope( + package.target.buildSettings, + environment: BuildEnvironment(platform: .macOS, configuration: .release) + ) + XCTAssertEqual(macosReleaseScope.evaluate(.OTHER_SWIFT_FLAGS), []) + } + } + } } final class PackageBuilderTester { diff --git a/Tests/WorkspaceTests/ManifestSourceGenerationTests.swift b/Tests/WorkspaceTests/ManifestSourceGenerationTests.swift index 88652873d23..59150ce0a5e 100644 --- a/Tests/WorkspaceTests/ManifestSourceGenerationTests.swift +++ b/Tests/WorkspaceTests/ManifestSourceGenerationTests.swift @@ -589,4 +589,38 @@ class ManifestSourceGenerationTests: XCTestCase { let contents = try manifest.generateManifestFileContents(packageDirectory: manifest.path.parentDirectory) try await testManifestWritingRoundTrip(manifestContents: contents, toolsVersion: .v5_9) } + + func testManifestGenerationWithSwiftLanguageVersion() async throws { + let manifest = Manifest.createRootManifest( + displayName: "pkg", + path: "/pkg", + toolsVersion: .v6_0, + dependencies: [], + targets: [ + try TargetDescription( + name: "v5", + type: .executable, + settings: [ + .init(tool: .swift, kind: .swiftLanguageVersion(.v5)) + ] + ), + try TargetDescription( + name: "custom", + type: .executable, + settings: [ + .init(tool: .swift, kind: .swiftLanguageVersion(.init(string: "5.10")!)) + ] + ), + try TargetDescription( + name: "conditional", + type: .executable, + settings: [ + .init(tool: .swift, kind: .swiftLanguageVersion(.v5), condition: .init(platformNames: ["linux"])), + .init(tool: .swift, kind: .swiftLanguageVersion(.v4), condition: .init(platformNames: ["macos"], config: "debug")) + ] + ) + ]) + let contents = try manifest.generateManifestFileContents(packageDirectory: manifest.path.parentDirectory) + try await testManifestWritingRoundTrip(manifestContents: contents, toolsVersion: .v6_0) + } } From 73769def68b08f70ddb7f463d4da655afcead875 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 4 Apr 2024 09:57:43 -0700 Subject: [PATCH 080/159] Add Swift language version 6 to Swift{Language}Version structs --- Sources/PackageDescription/LanguageStandardSettings.swift | 5 +++++ .../PackageDescription/PackageDescriptionSerialization.swift | 1 + .../PackageDescriptionSerializationConversion.swift | 1 + Sources/PackageLoading/ManifestJSONParser.swift | 1 + Sources/PackageModel/SwiftLanguageVersion.swift | 5 ++++- Tests/WorkspaceTests/ManifestSourceGenerationTests.swift | 2 +- 6 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Sources/PackageDescription/LanguageStandardSettings.swift b/Sources/PackageDescription/LanguageStandardSettings.swift index e7b61c2cdc6..c1346f67a87 100644 --- a/Sources/PackageDescription/LanguageStandardSettings.swift +++ b/Sources/PackageDescription/LanguageStandardSettings.swift @@ -167,6 +167,10 @@ public enum SwiftVersion { @available(_PackageDescription, introduced: 5) case v5 + /// The identifier for the Swift 6 language version. + @available(_PackageDescription, introduced: 6) + case v6 + /// A user-defined value for the Swift version. /// /// The value is passed as-is to the Swift compiler's `-swift-version` flag. @@ -180,6 +184,7 @@ extension SwiftVersion: CustomStringConvertible { case .v4: "4" case .v4_2: "4.2" case .v5: "5" + case .v6: "6" case .version(let version): version } } diff --git a/Sources/PackageDescription/PackageDescriptionSerialization.swift b/Sources/PackageDescription/PackageDescriptionSerialization.swift index a16cc995b81..ad852b9d859 100644 --- a/Sources/PackageDescription/PackageDescriptionSerialization.swift +++ b/Sources/PackageDescription/PackageDescriptionSerialization.swift @@ -99,6 +99,7 @@ enum Serialization { case v4 case v4_2 case v5 + case v6 case version(String) } diff --git a/Sources/PackageDescription/PackageDescriptionSerializationConversion.swift b/Sources/PackageDescription/PackageDescriptionSerializationConversion.swift index 26459c345a9..e85d9c0cd1f 100644 --- a/Sources/PackageDescription/PackageDescriptionSerializationConversion.swift +++ b/Sources/PackageDescription/PackageDescriptionSerializationConversion.swift @@ -112,6 +112,7 @@ extension Serialization.SwiftVersion { case .v4: self = .v4 case .v4_2: self = .v4_2 case .v5: self = .v5 + case .v6: self = .v6 case .version(let version): self = .version(version) } } diff --git a/Sources/PackageLoading/ManifestJSONParser.swift b/Sources/PackageLoading/ManifestJSONParser.swift index e989b64e692..564e5991982 100644 --- a/Sources/PackageLoading/ManifestJSONParser.swift +++ b/Sources/PackageLoading/ManifestJSONParser.swift @@ -150,6 +150,7 @@ enum ManifestJSONParser { case .v4: languageVersionString = "4" case .v4_2: languageVersionString = "4.2" case .v5: languageVersionString = "5" + case .v6: languageVersionString = "6" case .version(let version): languageVersionString = version } guard let languageVersion = SwiftLanguageVersion(string: languageVersionString) else { diff --git a/Sources/PackageModel/SwiftLanguageVersion.swift b/Sources/PackageModel/SwiftLanguageVersion.swift index 2aabc03c138..e7a1870c3a6 100644 --- a/Sources/PackageModel/SwiftLanguageVersion.swift +++ b/Sources/PackageModel/SwiftLanguageVersion.swift @@ -31,9 +31,12 @@ public struct SwiftLanguageVersion: Hashable, Sendable { /// Swift language version 5. public static let v5 = SwiftLanguageVersion(uncheckedString: "5") + /// Swift language version 6. + public static let v6 = SwiftLanguageVersion(uncheckedString: "6") + /// The list of known Swift language versions. public static let knownSwiftLanguageVersions = [ - v3, v4, v4_2, v5, + v3, v4, v4_2, v5, v6 ] /// The raw value of the language version. diff --git a/Tests/WorkspaceTests/ManifestSourceGenerationTests.swift b/Tests/WorkspaceTests/ManifestSourceGenerationTests.swift index 59150ce0a5e..a1643f75595 100644 --- a/Tests/WorkspaceTests/ManifestSourceGenerationTests.swift +++ b/Tests/WorkspaceTests/ManifestSourceGenerationTests.swift @@ -601,7 +601,7 @@ class ManifestSourceGenerationTests: XCTestCase { name: "v5", type: .executable, settings: [ - .init(tool: .swift, kind: .swiftLanguageVersion(.v5)) + .init(tool: .swift, kind: .swiftLanguageVersion(.v6)) ] ), try TargetDescription( From 812fa45febf0c2a25f403050f6269f4da5c7f75a Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 12 Apr 2024 18:05:13 +0100 Subject: [PATCH 081/159] Expose `computeTestTargetsForExecutableTargets` via SPI (#7455) Bringing back `computeTestTargetsForExecutableTargets` on `ModulesGraph` via `@_spi(SwiftPMInternal)`. --- Sources/PackageGraph/ModulesGraph.swift | 47 +++++++++++++++++++------ Tests/FunctionalTests/PluginTests.swift | 7 ++-- 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/Sources/PackageGraph/ModulesGraph.swift b/Sources/PackageGraph/ModulesGraph.swift index 84f1bcd4907..5103167a0ba 100644 --- a/Sources/PackageGraph/ModulesGraph.swift +++ b/Sources/PackageGraph/ModulesGraph.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2014-2021 Apple Inc. and the Swift project authors +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information @@ -23,13 +23,31 @@ enum PackageGraphError: Swift.Error { case cycleDetected((path: [Manifest], cycle: [Manifest])) /// The product dependency not found. - case productDependencyNotFound(package: String, targetName: String, dependencyProductName: String, dependencyPackageName: String?, dependencyProductInDecl: Bool, similarProductName: String?, packageContainingSimilarProduct: String?) + case productDependencyNotFound( + package: String, + targetName: String, + dependencyProductName: String, + dependencyPackageName: String?, + dependencyProductInDecl: Bool, + similarProductName: String?, + packageContainingSimilarProduct: String? + ) /// The package dependency already satisfied by a different dependency package - case dependencyAlreadySatisfiedByIdentifier(package: String, dependencyLocation: String, otherDependencyURL: String, identity: PackageIdentity) + case dependencyAlreadySatisfiedByIdentifier( + package: String, + dependencyLocation: String, + otherDependencyURL: String, + identity: PackageIdentity + ) /// The package dependency already satisfied by a different dependency package - case dependencyAlreadySatisfiedByName(package: String, dependencyLocation: String, otherDependencyURL: String, name: String) + case dependencyAlreadySatisfiedByName( + package: String, + dependencyLocation: String, + otherDependencyURL: String, + name: String + ) /// The product dependency was found but the package name was not referenced correctly (tools version > 5.2). case productDependencyMissingPackage( @@ -38,15 +56,23 @@ enum PackageGraphError: Swift.Error { packageIdentifier: String ) /// Dependency between a plugin and a dependent target/product of a given type is unsupported - case unsupportedPluginDependency(targetName: String, dependencyName: String, dependencyType: String, dependencyPackage: String?) + case unsupportedPluginDependency( + targetName: String, + dependencyName: String, + dependencyType: String, + dependencyPackage: String? + ) + /// A product was found in multiple packages. case duplicateProduct(product: String, packages: [Package]) /// Duplicate aliases for a target found in a product. - case multipleModuleAliases(target: String, - product: String, - package: String, - aliases: [String]) + case multipleModuleAliases( + target: String, + product: String, + package: String, + aliases: [String] + ) } @available(*, @@ -187,7 +213,8 @@ public struct ModulesGraph { } /// Computes a map from each executable target in any of the root packages to the corresponding test targets. - func computeTestTargetsForExecutableTargets() throws -> [ResolvedTarget.ID: [ResolvedTarget]] { + @_spi(SwiftPMInternal) + public func computeTestTargetsForExecutableTargets() throws -> [ResolvedTarget.ID: [ResolvedTarget]] { var result = [ResolvedTarget.ID: [ResolvedTarget]]() let rootTargets = IdentifiableSet(rootPackages.flatMap { $0.targets }) diff --git a/Tests/FunctionalTests/PluginTests.swift b/Tests/FunctionalTests/PluginTests.swift index 6118eae783d..c9bb4794df6 100644 --- a/Tests/FunctionalTests/PluginTests.swift +++ b/Tests/FunctionalTests/PluginTests.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2021-2022 Apple Inc. and the Swift project authors +// Copyright (c) 2021-2024 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information @@ -11,6 +11,8 @@ //===----------------------------------------------------------------------===// import Basics + +@_spi(SwiftPMInternal) @testable import PackageGraph import PackageLoading import PackageModel @@ -19,8 +21,7 @@ import SPMTestSupport import Workspace import XCTest -class PluginTests: XCTestCase { - +final class PluginTests: XCTestCase { func testUseOfBuildToolPluginTargetByExecutableInSamePackage() throws { // Only run the test if the environment in which we're running actually supports Swift concurrency (which the plugin APIs require). try XCTSkipIf(!UserToolchain.default.supportsSwiftConcurrency(), "skipping because test environment doesn't support concurrency") From 1c68e6cab56a3acba30d3ecea0c09209c6c7f565 Mon Sep 17 00:00:00 2001 From: Ryu <87907656+Ryu0118@users.noreply.github.com> Date: Sat, 13 Apr 2024 06:59:30 +0900 Subject: [PATCH 082/159] Add VisionOS platform support to `PIF.PlatformFilter` (#7448) #### Motivation: In the process of generating PIF, I identified a missing case for VisionOS in the implementation of the `[PackageConditionProtocol].toPlatformFilters()`, which led to assertionFailure when Package's platforms included VisionOS. #### Modifications: - Added a new case `.visionOS` in the switch statement within the `toPlatformFilters()` extension for `[PackageCondition]`. - Added a new static property `visionOSFilters` within `PIF.PlatformFilter` to define filters specific to VisionOS. #### Result: With these changes, PIFBuilder can now generate PIF without errors for packages that include VisionOS as a target --- Sources/XCBuildSupport/PIFBuilder.swift | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Sources/XCBuildSupport/PIFBuilder.swift b/Sources/XCBuildSupport/PIFBuilder.swift index dffbc7cbe9f..dfa8937abaf 100644 --- a/Sources/XCBuildSupport/PIFBuilder.swift +++ b/Sources/XCBuildSupport/PIFBuilder.swift @@ -1583,6 +1583,9 @@ extension [PackageCondition] { case .watchOS: result += PIF.PlatformFilter.watchOSFilters + case .visionOS: + result += PIF.PlatformFilter.visionOSFilters + case .linux: result += PIF.PlatformFilter.linuxFilters @@ -1671,6 +1674,14 @@ extension PIF.PlatformFilter { public static let webAssemblyFilters: [PIF.PlatformFilter] = [ .init(platform: "wasi"), ] + + /// VisionOS platform filters. + public static let visionOSFilters: [PIF.PlatformFilter] = [ + .init(platform: "xros"), + .init(platform: "xros", environment: "simulator"), + .init(platform: "visionos"), + .init(platform: "visionos", environment: "simulator") + ] } private extension PIF.BuildSettings { From 27996b8ae75df9f35d45c6cc1929a7456c2dcb60 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Mon, 15 Apr 2024 17:38:46 +0100 Subject: [PATCH 083/159] Rename `ResolvedTarget` to `ResolvedModule` (#7459) ### Motivation: With host/target triples separation in the SwiftPM codebase, it gets very confusing whether at a given moment "target" refers to a module, a triple, or a low level build system target. ### Modifications: Renamed `ResolvedTarget` to `ResolvedModule`. Added a deprecated `typealias ResolvedTarget = ResolvedModule` to allow graceful migration for users of this type. ### Result: Confusion between target triples and package targets is reduced. This has no impact on how these concepts are named in user-visible APIs like `PackageDescription` and `PackagePlugin`, target there can stay as "target" for as long as needed. Remaining internal uses of "target" outside of "target triple" context, like `*TargetBuildDescription` will be renamed in future PRs. --- .../ClangTargetBuildDescription.swift | 6 +-- .../BuildDescription/PluginDescription.swift | 2 +- .../ProductBuildDescription.swift | 2 +- .../SwiftTargetBuildDescription.swift | 4 +- .../TargetBuildDescription.swift | 4 +- .../LLBuildManifestBuilder+Clang.swift | 4 +- .../LLBuildManifestBuilder+Swift.swift | 8 ++-- .../LLBuildManifestBuilder.swift | 2 +- Sources/Build/BuildOperation.swift | 6 +-- .../Build/BuildPlan/BuildPlan+Product.swift | 14 +++---- Sources/Build/BuildPlan/BuildPlan+Test.swift | 16 ++++---- Sources/Build/BuildPlan/BuildPlan.swift | 32 +++++++-------- .../PackageCommands/DumpCommands.swift | 2 +- .../Commands/Utilities/PluginDelegate.swift | 2 +- .../Utilities/SymbolGraphExtract.swift | 10 ++--- .../PackageGraph/ModulesGraph+Loading.swift | 6 +-- Sources/PackageGraph/ModulesGraph.swift | 28 ++++++------- .../Resolution/PlatformVersionProvider.swift | 2 +- .../Resolution/ResolvedPackage.swift | 4 +- .../Resolution/ResolvedProduct.swift | 14 +++---- .../Resolution/ResolvedTarget.swift | 40 +++++++++++-------- .../BuildSystem/BuildSystem.swift | 2 +- .../Plugins/PluginContextSerializer.swift | 2 +- .../Plugins/PluginInvocation.swift | 2 +- .../ResolvedPackage+Extensions.swift | 4 +- .../SourceKitLSPAPI/BuildDescription.swift | 4 +- .../PluginTargetBuildDescription.swift | 6 +-- Sources/XCBuildSupport/PIFBuilder.swift | 30 +++++++------- 28 files changed, 132 insertions(+), 126 deletions(-) diff --git a/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift b/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift index 1387d2933f4..4c62450d3c3 100644 --- a/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift @@ -15,7 +15,7 @@ import PackageGraph import PackageLoading import PackageModel import struct PackageGraph.ModulesGraph -import struct PackageGraph.ResolvedTarget +import struct PackageGraph.ResolvedModule import struct SPMBuildCore.BuildParameters import struct SPMBuildCore.BuildToolPluginInvocationResult import struct SPMBuildCore.PrebuildCommandResult @@ -28,7 +28,7 @@ package final class ClangTargetBuildDescription { package let package: ResolvedPackage /// The target described by this target. - package let target: ResolvedTarget + package let target: ResolvedModule /// The underlying clang target. package let clangTarget: ClangTarget @@ -114,7 +114,7 @@ package final class ClangTargetBuildDescription { /// Create a new target description with target and build parameters. init( package: ResolvedPackage, - target: ResolvedTarget, + target: ResolvedModule, toolsVersion: ToolsVersion, additionalFileRules: [FileRuleDescription] = [], buildParameters: BuildParameters, diff --git a/Sources/Build/BuildDescription/PluginDescription.swift b/Sources/Build/BuildDescription/PluginDescription.swift index 72614a96239..8049d272274 100644 --- a/Sources/Build/BuildDescription/PluginDescription.swift +++ b/Sources/Build/BuildDescription/PluginDescription.swift @@ -43,7 +43,7 @@ package final class PluginDescription: Codable { /// Initialize a new plugin target description. The target is expected to be /// a `PluginTarget`. init( - target: ResolvedTarget, + target: ResolvedModule, products: [ResolvedProduct], package: ResolvedPackage, toolsVersion: ToolsVersion, diff --git a/Sources/Build/BuildDescription/ProductBuildDescription.swift b/Sources/Build/BuildDescription/ProductBuildDescription.swift index 88fa223eee9..2c5538cbb3e 100644 --- a/Sources/Build/BuildDescription/ProductBuildDescription.swift +++ b/Sources/Build/BuildDescription/ProductBuildDescription.swift @@ -49,7 +49,7 @@ package final class ProductBuildDescription: SPMBuildCore.ProductBuildDescriptio var additionalFlags: [String] = [] /// The list of targets that are going to be linked statically in this product. - var staticTargets: [ResolvedTarget] = [] + var staticTargets: [ResolvedModule] = [] /// The list of Swift modules that should be passed to the linker. This is required for debugging to work. var swiftASTs: SortedArray = .init() diff --git a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift index 7a7572aa1f0..1b5777bcb80 100644 --- a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift @@ -33,7 +33,7 @@ package final class SwiftTargetBuildDescription { package let package: ResolvedPackage /// The target described by this target. - package let target: ResolvedTarget + package let target: ResolvedModule private let swiftTarget: SwiftTarget @@ -248,7 +248,7 @@ package final class SwiftTargetBuildDescription { /// Create a new target description with target and build parameters. init( package: ResolvedPackage, - target: ResolvedTarget, + target: ResolvedModule, toolsVersion: ToolsVersion, additionalFileRules: [FileRuleDescription] = [], buildParameters: BuildParameters, diff --git a/Sources/Build/BuildDescription/TargetBuildDescription.swift b/Sources/Build/BuildDescription/TargetBuildDescription.swift index 04462cf57b4..c5e14b9ac9e 100644 --- a/Sources/Build/BuildDescription/TargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/TargetBuildDescription.swift @@ -11,7 +11,7 @@ //===----------------------------------------------------------------------===// import Basics -import struct PackageGraph.ResolvedTarget +import struct PackageGraph.ResolvedModule import struct PackageModel.Resource import struct PackageModel.ToolsVersion import struct SPMBuildCore.BuildToolPluginInvocationResult @@ -61,7 +61,7 @@ package enum TargetBuildDescription { } } - var target: ResolvedTarget { + var target: ResolvedModule { switch self { case .swift(let target): return target.target diff --git a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Clang.swift b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Clang.swift index 16f79dc0f35..70ae4ee508a 100644 --- a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Clang.swift +++ b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Clang.swift @@ -14,7 +14,7 @@ import struct LLBuildManifest.Node import struct Basics.AbsolutePath import struct Basics.InternalError import class Basics.ObservabilityScope -import struct PackageGraph.ResolvedTarget +import struct PackageGraph.ResolvedModule import PackageModel extension LLBuildManifestBuilder { @@ -32,7 +32,7 @@ extension LLBuildManifestBuilder { inputs.append(resourcesNode) } - func addStaticTargetInputs(_ target: ResolvedTarget) { + func addStaticTargetInputs(_ target: ResolvedModule) { if case .swift(let desc)? = self.plan.targetMap[target.id], target.type == .library { inputs.append(file: desc.moduleOutputPath) } diff --git a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift index 144623b3f1d..d499d231ebb 100644 --- a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift +++ b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift @@ -17,7 +17,7 @@ import struct Basics.TSCAbsolutePath import struct LLBuildManifest.Node import struct LLBuildManifest.LLBuildManifest import struct SPMBuildCore.BuildParameters -import struct PackageGraph.ResolvedTarget +import struct PackageGraph.ResolvedModule import protocol TSCBasic.FileSystem import enum TSCBasic.ProcessEnv import func TSCBasic.topologicalSort @@ -191,9 +191,9 @@ extension LLBuildManifestBuilder { package func addTargetsToExplicitBuildManifest() throws { // Sort the product targets in topological order in order to collect and "bubble up" // their respective dependency graphs to the depending targets. - let nodes: [ResolvedTarget.Dependency] = try self.plan.targetMap.keys.compactMap { + let nodes: [ResolvedModule.Dependency] = try self.plan.targetMap.keys.compactMap { guard let target = self.plan.graph.allTargets[$0] else { throw InternalError("unknown target \($0)") } - return ResolvedTarget.Dependency.target(target, conditions: []) + return ResolvedModule.Dependency.target(target, conditions: []) } let allPackageDependencies = try topologicalSort(nodes, successors: { $0.dependencies }) // Instantiate the inter-module dependency oracle which will cache commonly-scanned @@ -415,7 +415,7 @@ extension LLBuildManifestBuilder { inputs.append(resourcesNode) } - func addStaticTargetInputs(_ target: ResolvedTarget) throws { + func addStaticTargetInputs(_ target: ResolvedModule) throws { // Ignore C Modules. if target.underlying is SystemLibraryTarget { return } // Ignore Binary Modules. diff --git a/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift b/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift index 73966ce6bf9..b1d781b7eb2 100644 --- a/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift +++ b/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift @@ -316,7 +316,7 @@ extension TargetBuildDescription { } } -extension ResolvedTarget { +extension ResolvedModule { package func getCommandName(config: String) -> String { "C." + self.getLLBuildTargetName(config: config) } diff --git a/Sources/Build/BuildOperation.swift b/Sources/Build/BuildOperation.swift index 847f941bc46..e4f4128d46c 100644 --- a/Sources/Build/BuildOperation.swift +++ b/Sources/Build/BuildOperation.swift @@ -535,8 +535,8 @@ package final class BuildOperation: PackageStructureDelegate, SPMBuildCore.Build private func plan(subset: BuildSubset? = nil) throws -> (description: BuildDescription, manifest: LLBuildManifest) { // Load the package graph. let graph = try getPackageGraph() - let buildToolPluginInvocationResults: [ResolvedTarget.ID: (target: ResolvedTarget, results: [BuildToolPluginInvocationResult])] - let prebuildCommandResults: [ResolvedTarget.ID: [PrebuildCommandResult]] + let buildToolPluginInvocationResults: [ResolvedModule.ID: (target: ResolvedModule, results: [BuildToolPluginInvocationResult])] + let prebuildCommandResults: [ResolvedModule.ID: [PrebuildCommandResult]] // Invoke any build tool plugins in the graph to generate prebuild commands and build commands. if let pluginConfiguration, !self.productsBuildParameters.shouldSkipBuilding { // Hacky workaround for rdar://120560817, but it replicates precisely enough the original behavior before @@ -879,7 +879,7 @@ extension BuildDescription { } extension BuildSubset { - func recursiveDependencies(for graph: ModulesGraph, observabilityScope: ObservabilityScope) throws -> [ResolvedTarget]? { + func recursiveDependencies(for graph: ModulesGraph, observabilityScope: ObservabilityScope) throws -> [ResolvedModule]? { switch self { case .allIncludingTests: return Array(graph.reachableTargets) diff --git a/Sources/Build/BuildPlan/BuildPlan+Product.swift b/Sources/Build/BuildPlan/BuildPlan+Product.swift index 436375f8d80..6031c30775b 100644 --- a/Sources/Build/BuildPlan/BuildPlan+Product.swift +++ b/Sources/Build/BuildPlan/BuildPlan+Product.swift @@ -14,7 +14,7 @@ import struct Basics.AbsolutePath import struct Basics.Triple import struct Basics.InternalError import struct PackageGraph.ResolvedProduct -import struct PackageGraph.ResolvedTarget +import struct PackageGraph.ResolvedModule import class PackageModel.BinaryTarget import class PackageModel.ClangTarget @@ -124,8 +124,8 @@ extension BuildPlan { buildParameters: BuildParameters ) throws -> ( dylibs: [ResolvedProduct], - staticTargets: [ResolvedTarget], - systemModules: [ResolvedTarget], + staticTargets: [ResolvedModule], + systemModules: [ResolvedModule], libraryBinaryPaths: Set, availableTools: [String: AbsolutePath] ) { @@ -160,7 +160,7 @@ extension BuildPlan { } // Sort the product targets in topological order. - let nodes: [ResolvedTarget.Dependency] = product.targets.map { .target($0, conditions: []) } + let nodes: [ResolvedModule.Dependency] = product.targets.map { .target($0, conditions: []) } let allTargets = try topologicalSort(nodes, successors: { dependency in switch dependency { // Include all the dependencies of a target. @@ -185,7 +185,7 @@ extension BuildPlan { return [] } - let productDependencies: [ResolvedTarget.Dependency] = product.targets.map { .target($0, conditions: []) } + let productDependencies: [ResolvedModule.Dependency] = product.targets.map { .target($0, conditions: []) } switch product.type { case .library(.automatic), .library(.static): return productDependencies @@ -201,8 +201,8 @@ extension BuildPlan { // Create empty arrays to collect our results. var linkLibraries = [ResolvedProduct]() - var staticTargets = [ResolvedTarget]() - var systemModules = [ResolvedTarget]() + var staticTargets = [ResolvedModule]() + var systemModules = [ResolvedModule]() var libraryBinaryPaths: Set = [] var availableTools = [String: AbsolutePath]() diff --git a/Sources/Build/BuildPlan/BuildPlan+Test.swift b/Sources/Build/BuildPlan/BuildPlan+Test.swift index 58001c2816f..bf1838af200 100644 --- a/Sources/Build/BuildPlan/BuildPlan+Test.swift +++ b/Sources/Build/BuildPlan/BuildPlan+Test.swift @@ -17,7 +17,7 @@ import struct LLBuildManifest.TestDiscoveryTool import struct LLBuildManifest.TestEntryPointTool import struct PackageGraph.ModulesGraph import struct PackageGraph.ResolvedProduct -import struct PackageGraph.ResolvedTarget +import struct PackageGraph.ResolvedModule import struct PackageModel.Sources import class PackageModel.SwiftTarget import class PackageModel.Target @@ -66,7 +66,7 @@ extension BuildPlan { } /// Generates test discovery targets, which contain derived sources listing the discovered tests. - func generateDiscoveryTargets() throws -> (target: SwiftTarget, resolved: ResolvedTarget, buildDescription: SwiftTargetBuildDescription) { + func generateDiscoveryTargets() throws -> (target: SwiftTarget, resolved: ResolvedModule, buildDescription: SwiftTargetBuildDescription) { let discoveryTargetName = "\(package.manifest.displayName)PackageDiscoveredTests" let discoveryDerivedDir = buildParameters.buildPath.appending(components: "\(discoveryTargetName).derived") let discoveryMainFile = discoveryDerivedDir.appending(component: TestDiscoveryTool.mainFileName) @@ -84,7 +84,7 @@ extension BuildPlan { packageAccess: true, // test target is allowed access to package decls by default testDiscoverySrc: Sources(paths: discoveryPaths, root: discoveryDerivedDir) ) - let discoveryResolvedTarget = ResolvedTarget( + let discoveryResolvedTarget = ResolvedModule( packageIdentity: testProduct.packageIdentity, underlying: discoveryTarget, dependencies: testProduct.targets.map { .target($0, conditions: []) }, @@ -110,7 +110,7 @@ extension BuildPlan { /// point API and leverages the test discovery target to reference which tests to run. func generateSynthesizedEntryPointTarget( swiftTargetDependencies: [Target.Dependency], - resolvedTargetDependencies: [ResolvedTarget.Dependency] + resolvedTargetDependencies: [ResolvedModule.Dependency] ) throws -> SwiftTargetBuildDescription { let entryPointDerivedDir = buildParameters.buildPath.appending(components: "\(testProduct.name).derived") let entryPointMainFileName = TestEntryPointTool.mainFileName(for: buildParameters.testingParameters.library) @@ -124,7 +124,7 @@ extension BuildPlan { packageAccess: true, // test target is allowed access to package decls testEntryPointSources: entryPointSources ) - let entryPointResolvedTarget = ResolvedTarget( + let entryPointResolvedTarget = ResolvedModule( packageIdentity: testProduct.packageIdentity, underlying: entryPointTarget, dependencies: testProduct.targets.map { .target($0, conditions: []) } + resolvedTargetDependencies, @@ -144,9 +144,9 @@ extension BuildPlan { ) } - let discoveryTargets: (target: SwiftTarget, resolved: ResolvedTarget, buildDescription: SwiftTargetBuildDescription)? + let discoveryTargets: (target: SwiftTarget, resolved: ResolvedModule, buildDescription: SwiftTargetBuildDescription)? let swiftTargetDependencies: [Target.Dependency] - let resolvedTargetDependencies: [ResolvedTarget.Dependency] + let resolvedTargetDependencies: [ResolvedModule.Dependency] switch buildParameters.testingParameters.library { case .xctest: @@ -169,7 +169,7 @@ extension BuildPlan { packageAccess: entryPointResolvedTarget.packageAccess, testEntryPointSources: entryPointResolvedTarget.underlying.sources ) - let entryPointResolvedTarget = ResolvedTarget( + let entryPointResolvedTarget = ResolvedModule( packageIdentity: testProduct.packageIdentity, underlying: entryPointTarget, dependencies: entryPointResolvedTarget.dependencies + resolvedTargetDependencies, diff --git a/Sources/Build/BuildPlan/BuildPlan.swift b/Sources/Build/BuildPlan/BuildPlan.swift index 5f6b8389c3f..0c13a3daed9 100644 --- a/Sources/Build/BuildPlan/BuildPlan.swift +++ b/Sources/Build/BuildPlan/BuildPlan.swift @@ -103,7 +103,7 @@ extension BuildParameters { } /// Returns the compiler arguments for the index store, if enabled. - func indexStoreArguments(for target: ResolvedTarget) -> [String] { + func indexStoreArguments(for target: ResolvedModule) -> [String] { let addIndexStoreArguments: Bool switch indexStoreMode { case .on: @@ -128,7 +128,7 @@ extension BuildParameters { } /// Computes the target triple arguments for a given resolved target. - package func targetTripleArgs(for target: ResolvedTarget) throws -> [String] { + package func targetTripleArgs(for target: ResolvedModule) throws -> [String] { var args = ["-target"] // Compute the triple string for Darwin platform using the platform version. @@ -144,7 +144,7 @@ extension BuildParameters { /// Computes the linker flags to use in order to rename a module-named main function to 'main' for the target /// platform, or nil if the linker doesn't support it for the platform. - func linkerFlagsForRenamingMainFunction(of target: ResolvedTarget) -> [String]? { + func linkerFlagsForRenamingMainFunction(of target: ResolvedModule) -> [String]? { let args: [String] if self.triple.isApple() { args = ["-alias", "_\(target.c99name)_main", "_main"] @@ -157,7 +157,7 @@ extension BuildParameters { } /// Returns the scoped view of build settings for a given target. - func createScope(for target: ResolvedTarget) -> BuildSettings.Scope { + func createScope(for target: ResolvedModule) -> BuildSettings.Scope { BuildSettings.Scope(target.underlying.buildSettings, environment: buildEnvironment) } } @@ -186,7 +186,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan { public let toolsBuildParameters: BuildParameters /// Triple for which this target is compiled. - private func buildTriple(for target: ResolvedTarget) -> Basics.Triple { + private func buildTriple(for target: ResolvedModule) -> Basics.Triple { self.buildParameters(for: target).triple } @@ -199,7 +199,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan { package let graph: ModulesGraph /// The target build description map. - package let targetMap: [ResolvedTarget.ID: TargetBuildDescription] + package let targetMap: [ResolvedModule.ID: TargetBuildDescription] /// The product build description map. package let productMap: [ResolvedProduct.ID: ProductBuildDescription] @@ -219,13 +219,13 @@ public class BuildPlan: SPMBuildCore.BuildPlan { } /// The results of invoking any build tool plugins used by targets in this build. - package let buildToolPluginInvocationResults: [ResolvedTarget.ID: [BuildToolPluginInvocationResult]] + package let buildToolPluginInvocationResults: [ResolvedModule.ID: [BuildToolPluginInvocationResult]] /// The results of running any prebuild commands for the targets in this build. This includes any derived /// source files as well as directories to which any changes should cause us to reevaluate the build plan. - package let prebuildCommandResults: [ResolvedTarget.ID: [PrebuildCommandResult]] + package let prebuildCommandResults: [ResolvedModule.ID: [PrebuildCommandResult]] - package private(set) var derivedTestTargetsMap: [ResolvedProduct.ID: [ResolvedTarget]] = [:] + package private(set) var derivedTestTargetsMap: [ResolvedProduct.ID: [ResolvedModule]] = [:] /// Cache for pkgConfig flags. private var pkgConfigCache = [SystemLibraryTarget: (cFlags: [String], libs: [String])]() @@ -273,8 +273,8 @@ public class BuildPlan: SPMBuildCore.BuildPlan { toolsBuildParameters: BuildParameters, graph: ModulesGraph, additionalFileRules: [FileRuleDescription] = [], - buildToolPluginInvocationResults: [ResolvedTarget.ID: [BuildToolPluginInvocationResult]] = [:], - prebuildCommandResults: [ResolvedTarget.ID: [PrebuildCommandResult]] = [:], + buildToolPluginInvocationResults: [ResolvedModule.ID: [BuildToolPluginInvocationResult]] = [:], + prebuildCommandResults: [ResolvedModule.ID: [PrebuildCommandResult]] = [:], disableSandbox: Bool = false, fileSystem: any FileSystem, observabilityScope: ObservabilityScope @@ -315,7 +315,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan { )) } let macroProductsByTarget = productMap.values.filter { $0.product.type == .macro } - .reduce(into: [ResolvedTarget.ID: ResolvedProduct]()) { + .reduce(into: [ResolvedModule.ID: ResolvedProduct]()) { if let target = $1.product.targets.first { $0[target.id] = $1.product } @@ -325,7 +325,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan { // Plugin targets are noted, since they need to be compiled, but they do // not get directly incorporated into the build description that will be // given to LLBuild. - var targetMap = [ResolvedTarget.ID: TargetBuildDescription]() + var targetMap = [ResolvedModule.ID: TargetBuildDescription]() var pluginDescriptions = [PluginDescription]() var shouldGenerateTestObservation = true for target in graph.allTargets.sorted(by: { $0.name < $1.name }) { @@ -473,7 +473,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan { static func validateDeploymentVersionOfProductDependency( product: ResolvedProduct, - forTarget target: ResolvedTarget, + forTarget target: ResolvedModule, buildEnvironment: BuildEnvironment, observabilityScope: ObservabilityScope ) throws { @@ -661,7 +661,7 @@ extension Basics.Diagnostic { } static func productRequiresHigherPlatformVersion( - target: ResolvedTarget, + target: ResolvedModule, targetPlatform: SupportedPlatform, product: String, productPlatform: SupportedPlatform @@ -693,7 +693,7 @@ extension BuildParameters { /// Generate the resource bundle Info.plist. func generateResourceInfoPlist( fileSystem: FileSystem, - target: ResolvedTarget, + target: ResolvedModule, path: AbsolutePath ) throws -> Bool { guard let defaultLocalization = target.defaultLocalization else { diff --git a/Sources/Commands/PackageCommands/DumpCommands.swift b/Sources/Commands/PackageCommands/DumpCommands.swift index 1120058dc04..40db905b9a5 100644 --- a/Sources/Commands/PackageCommands/DumpCommands.swift +++ b/Sources/Commands/PackageCommands/DumpCommands.swift @@ -75,7 +75,7 @@ struct DumpSymbolGraph: SwiftCommand { for target in targets { print("-- Emitting symbol graph for", target.name) let result = try symbolGraphExtractor.extractSymbolGraph( - target: target, + module: target, buildPlan: buildPlan, outputRedirection: .collect(redirectStderr: true), outputDirectory: symbolGraphDirectory, diff --git a/Sources/Commands/Utilities/PluginDelegate.swift b/Sources/Commands/Utilities/PluginDelegate.swift index 0b68adf360b..30d50f87dae 100644 --- a/Sources/Commands/Utilities/PluginDelegate.swift +++ b/Sources/Commands/Utilities/PluginDelegate.swift @@ -430,7 +430,7 @@ final class PluginDelegate: PluginInvocationDelegate { // Run the symbol graph extractor on the target. let result = try symbolGraphExtractor.extractSymbolGraph( - target: target, + module: target, buildPlan: try buildSystem.buildPlan, outputRedirection: .collect, outputDirectory: outputDir, diff --git a/Sources/Commands/Utilities/SymbolGraphExtract.swift b/Sources/Commands/Utilities/SymbolGraphExtract.swift index 4d3ad975db2..e30fb813f1a 100644 --- a/Sources/Commands/Utilities/SymbolGraphExtract.swift +++ b/Sources/Commands/Utilities/SymbolGraphExtract.swift @@ -50,23 +50,23 @@ package struct SymbolGraphExtract { case json(pretty: Bool) } - /// Creates a symbol graph for `target` in `outputDirectory` using the build information from `buildPlan`. + /// Creates a symbol graph for `module` in `outputDirectory` using the build information from `buildPlan`. /// The `outputDirection` determines how the output from the tool subprocess is handled, and `verbosity` specifies /// how much console output to ask the tool to emit. package func extractSymbolGraph( - target: ResolvedTarget, + module: ResolvedModule, buildPlan: BuildPlan, outputRedirection: TSCBasic.Process.OutputRedirection = .none, outputDirectory: AbsolutePath, verboseOutput: Bool ) throws -> ProcessResult { - let buildParameters = buildPlan.buildParameters(for: target) + let buildParameters = buildPlan.buildParameters(for: module) try self.fileSystem.createDirectory(outputDirectory, recursive: true) // Construct arguments for extracting symbols for a single target. var commandLine = [self.tool.pathString] - commandLine += ["-module-name", target.c99name] - commandLine += try buildParameters.targetTripleArgs(for: target) + commandLine += ["-module-name", module.c99name] + commandLine += try buildParameters.targetTripleArgs(for: module) commandLine += try buildPlan.createAPIToolCommonArgs(includeLibrarySearchPaths: true) commandLine += ["-module-cache-path", try buildParameters.moduleCache.pathString] if verboseOutput { diff --git a/Sources/PackageGraph/ModulesGraph+Loading.swift b/Sources/PackageGraph/ModulesGraph+Loading.swift index 5c0ab4e4ea1..716ff676695 100644 --- a/Sources/PackageGraph/ModulesGraph+Loading.swift +++ b/Sources/PackageGraph/ModulesGraph+Loading.swift @@ -891,7 +891,7 @@ private final class ResolvedProductBuilder: ResolvedBuilder { } /// Builder for resolved target. -private final class ResolvedTargetBuilder: ResolvedBuilder { +private final class ResolvedTargetBuilder: ResolvedBuilder { /// Enumeration to represent target dependencies. enum Dependency { @@ -932,14 +932,14 @@ private final class ResolvedTargetBuilder: ResolvedBuilder { self.platformVersionProvider = platformVersionProvider } - override func constructImpl() throws -> ResolvedTarget { + override func constructImpl() throws -> ResolvedModule { let diagnosticsEmitter = self.observabilityScope.makeDiagnosticsEmitter() { var metadata = ObservabilityMetadata() metadata.targetName = target.name return metadata } - let dependencies = try self.dependencies.map { dependency -> ResolvedTarget.Dependency in + let dependencies = try self.dependencies.map { dependency -> ResolvedModule.Dependency in switch dependency { case .target(let targetBuilder, let conditions): return .target(try targetBuilder.construct(), conditions: conditions) diff --git a/Sources/PackageGraph/ModulesGraph.swift b/Sources/PackageGraph/ModulesGraph.swift index 5103167a0ba..fdf206d84e5 100644 --- a/Sources/PackageGraph/ModulesGraph.swift +++ b/Sources/PackageGraph/ModulesGraph.swift @@ -92,13 +92,13 @@ public struct ModulesGraph { public let packages: [ResolvedPackage] /// The list of all targets reachable from root targets. - public let reachableTargets: IdentifiableSet + public let reachableTargets: IdentifiableSet /// The list of all products reachable from root targets. public let reachableProducts: IdentifiableSet /// Returns all the targets in the graph, regardless if they are reachable from the root targets or not. - public let allTargets: IdentifiableSet + public let allTargets: IdentifiableSet /// Returns all the products in the graph, regardless if they are reachable from the root targets or not. @@ -111,12 +111,12 @@ public struct ModulesGraph { public let requiredDependencies: [PackageReference] /// Returns true if a given target is present in root packages and is not excluded for the given build environment. - public func isInRootPackages(_ target: ResolvedTarget, satisfying buildEnvironment: BuildEnvironment) -> Bool { + public func isInRootPackages(_ target: ResolvedModule, satisfying buildEnvironment: BuildEnvironment) -> Bool { // FIXME: This can be easily cached. - return rootPackages.reduce(into: IdentifiableSet()) { (accumulator: inout IdentifiableSet, package: ResolvedPackage) in + return rootPackages.reduce(into: IdentifiableSet()) { (accumulator: inout IdentifiableSet, package: ResolvedPackage) in let allDependencies = package.targets.flatMap { $0.dependencies } let unsatisfiedDependencies = allDependencies.filter { !$0.satisfies(buildEnvironment) } - let unsatisfiedDependencyTargets = unsatisfiedDependencies.compactMap { (dep: ResolvedTarget.Dependency) -> ResolvedTarget? in + let unsatisfiedDependencyTargets = unsatisfiedDependencies.compactMap { (dep: ResolvedModule.Dependency) -> ResolvedModule? in switch dep { case .target(let target, _): return target @@ -134,10 +134,10 @@ public struct ModulesGraph { return self.rootPackages.contains(id: package.id) } - private let targetsToPackages: [ResolvedTarget.ID: ResolvedPackage] - /// Returns the package that contains the target, or nil if the target isn't in the graph. - public func package(for target: ResolvedTarget) -> ResolvedPackage? { - return self.targetsToPackages[target.id] + private let modulesToPackages: [ResolvedModule.ID: ResolvedPackage] + /// Returns the package that contains the module, or nil if the module isn't in the graph. + public func package(for module: ResolvedModule) -> ResolvedPackage? { + return self.modulesToPackages[module.id] } @@ -169,11 +169,11 @@ public struct ModulesGraph { // Create a mapping from targets to the packages that define them. Here // we include all targets, including tests in non-root packages, since // this is intended for lookup and not traversal. - self.targetsToPackages = packages.reduce(into: [:], { partial, package in + self.modulesToPackages = packages.reduce(into: [:], { partial, package in package.targets.forEach{ partial[$0.id] = package } }) - let allTargets = IdentifiableSet(packages.flatMap({ package -> [ResolvedTarget] in + let allTargets = IdentifiableSet(packages.flatMap({ package -> [ResolvedModule] in if rootPackages.contains(id: package.id) { return package.targets } else { @@ -214,13 +214,13 @@ public struct ModulesGraph { /// Computes a map from each executable target in any of the root packages to the corresponding test targets. @_spi(SwiftPMInternal) - public func computeTestTargetsForExecutableTargets() throws -> [ResolvedTarget.ID: [ResolvedTarget]] { - var result = [ResolvedTarget.ID: [ResolvedTarget]]() + public func computeTestTargetsForExecutableTargets() throws -> [ResolvedModule.ID: [ResolvedModule]] { + var result = [ResolvedModule.ID: [ResolvedModule]]() let rootTargets = IdentifiableSet(rootPackages.flatMap { $0.targets }) // Create map of test target to set of its direct dependencies. - let testTargetDepMap: [ResolvedTarget.ID: IdentifiableSet] = try { + let testTargetDepMap: [ResolvedModule.ID: IdentifiableSet] = try { let testTargetDeps = rootTargets.filter({ $0.type == .test }).map({ ($0.id, IdentifiableSet($0.dependencies.compactMap { $0.target }.filter { $0.type != .plugin })) }) diff --git a/Sources/PackageGraph/Resolution/PlatformVersionProvider.swift b/Sources/PackageGraph/Resolution/PlatformVersionProvider.swift index 32a7e37a64f..c95a49b7f10 100644 --- a/Sources/PackageGraph/Resolution/PlatformVersionProvider.swift +++ b/Sources/PackageGraph/Resolution/PlatformVersionProvider.swift @@ -32,7 +32,7 @@ func merge(into partial: inout [SupportedPlatform], platforms: [SupportedPlatfor public struct PlatformVersionProvider: Hashable { public enum Implementation: Hashable { - case mergingFromTargets(IdentifiableSet) + case mergingFromTargets(IdentifiableSet) case customXCTestMinimumDeploymentTargets([PackageModel.Platform: PlatformVersion]) case minimumDeploymentTargetDefault } diff --git a/Sources/PackageGraph/Resolution/ResolvedPackage.swift b/Sources/PackageGraph/Resolution/ResolvedPackage.swift index bf8d81eac78..f1e8f93fe26 100644 --- a/Sources/PackageGraph/Resolution/ResolvedPackage.swift +++ b/Sources/PackageGraph/Resolution/ResolvedPackage.swift @@ -34,7 +34,7 @@ public struct ResolvedPackage { public let underlying: Package /// The targets contained in the package. - public let targets: [ResolvedTarget] + public let targets: [ResolvedModule] /// The products produced by the package. public let products: [ResolvedProduct] @@ -58,7 +58,7 @@ public struct ResolvedPackage { defaultLocalization: String?, supportedPlatforms: [SupportedPlatform], dependencies: [ResolvedPackage], - targets: [ResolvedTarget], + targets: [ResolvedModule], products: [ResolvedProduct], registryMetadata: RegistryReleaseMetadata?, platformVersionProvider: PlatformVersionProvider diff --git a/Sources/PackageGraph/Resolution/ResolvedProduct.swift b/Sources/PackageGraph/Resolution/ResolvedProduct.swift index 1208b5b0d7f..c03e20fffa2 100644 --- a/Sources/PackageGraph/Resolution/ResolvedProduct.swift +++ b/Sources/PackageGraph/Resolution/ResolvedProduct.swift @@ -30,10 +30,10 @@ public struct ResolvedProduct { public let underlying: Product /// The top level targets contained in this product. - public let targets: IdentifiableSet + public let targets: IdentifiableSet /// Executable target for test entry point file. - public let testEntryPointTarget: ResolvedTarget? + public let testEntryPointTarget: ResolvedModule? /// The default localization for resources. public let defaultLocalization: String? @@ -49,7 +49,7 @@ public struct ResolvedProduct { /// The main executable target of product. /// /// Note: This property is only valid for executable products. - public var executableTarget: ResolvedTarget { + public var executableTarget: ResolvedModule { get throws { guard self.type == .executable || self.type == .snippet || self.type == .macro else { throw InternalError("`executableTarget` should only be called for executable targets") @@ -63,7 +63,7 @@ public struct ResolvedProduct { } } - public init(packageIdentity: PackageIdentity, product: Product, targets: IdentifiableSet) { + public init(packageIdentity: PackageIdentity, product: Product, targets: IdentifiableSet) { assert(product.targets.count == targets.count && product.targets.map(\.name).sorted() == targets.map(\.name).sorted()) self.packageIdentity = packageIdentity self.underlying = product @@ -87,7 +87,7 @@ public struct ResolvedProduct { packageAccess: true, // entry point target so treated as a part of the package testEntryPointPath: testEntryPointPath ) - return ResolvedTarget( + return ResolvedModule( packageIdentity: packageIdentity, underlying: swiftTarget, dependencies: targets.map { .target($0, conditions: []) }, @@ -113,13 +113,13 @@ public struct ResolvedProduct { } /// Returns the recursive target dependencies. - public func recursiveTargetDependencies() throws -> [ResolvedTarget] { + public func recursiveTargetDependencies() throws -> [ResolvedModule] { let recursiveDependencies = try targets.lazy.flatMap { try $0.recursiveTargetDependencies() } return Array(IdentifiableSet(self.targets).union(recursiveDependencies)) } private static func computePlatforms( - targets: IdentifiableSet + targets: IdentifiableSet ) -> ([SupportedPlatform], PlatformVersionProvider) { let declaredPlatforms = targets.reduce(into: [SupportedPlatform]()) { partial, item in merge(into: &partial, platforms: item.supportedPlatforms) diff --git a/Sources/PackageGraph/Resolution/ResolvedTarget.swift b/Sources/PackageGraph/Resolution/ResolvedTarget.swift index 4e9fecaf804..b8854a8ba3b 100644 --- a/Sources/PackageGraph/Resolution/ResolvedTarget.swift +++ b/Sources/PackageGraph/Resolution/ResolvedTarget.swift @@ -12,17 +12,20 @@ import PackageModel -/// Represents a fully resolved target. All the dependencies for this target are also stored as resolved. -public struct ResolvedTarget { +@available(*, deprecated, renamed: "ResolvedModule") +public typealias ResolvedTarget = ResolvedModule + +/// Represents a fully resolved module. All the dependencies for this module are also stored as resolved. +public struct ResolvedModule { /// Represents dependency of a resolved target. public enum Dependency { /// Direct dependency of the target. This target is in the same package and should be statically linked. - case target(_ target: ResolvedTarget, conditions: [PackageCondition]) + case target(_ target: ResolvedModule, conditions: [PackageCondition]) /// The target depends on this product. case product(_ product: ResolvedProduct, conditions: [PackageCondition]) - public var target: ResolvedTarget? { + public var target: ResolvedModule? { switch self { case .target(let target, _): return target case .product: return nil @@ -44,7 +47,7 @@ public struct ResolvedTarget { } /// Returns the direct dependencies of the underlying dependency, across the package graph. - public var dependencies: [ResolvedTarget.Dependency] { + public var dependencies: [ResolvedModule.Dependency] { switch self { case .target(let target, _): return target.dependencies @@ -54,7 +57,7 @@ public struct ResolvedTarget { } /// Returns the direct dependencies of the underlying dependency, limited to the target's package. - public var packageDependencies: [ResolvedTarget.Dependency] { + public var packageDependencies: [ResolvedModule.Dependency] { switch self { case .target(let target, _): return target.dependencies @@ -86,7 +89,7 @@ public struct ResolvedTarget { } /// Returns the recursive target dependencies, across the whole package-graph. - public func recursiveTargetDependencies() throws -> [ResolvedTarget] { + public func recursiveTargetDependencies() throws -> [ResolvedModule] { try topologicalSort(self.dependencies) { $0.dependencies }.compactMap { $0.target } } @@ -150,7 +153,7 @@ public struct ResolvedTarget { public init( packageIdentity: PackageIdentity, underlying: Target, - dependencies: [ResolvedTarget.Dependency], + dependencies: [ResolvedModule.Dependency], defaultLocalization: String? = nil, supportedPlatforms: [SupportedPlatform], platformVersionProvider: PlatformVersionProvider @@ -173,13 +176,13 @@ public struct ResolvedTarget { } } -extension ResolvedTarget: CustomStringConvertible { +extension ResolvedModule: CustomStringConvertible { public var description: String { return "" } } -extension ResolvedTarget.Dependency: CustomStringConvertible { +extension ResolvedModule.Dependency: CustomStringConvertible { public var description: String { var str = " Bool { +extension ResolvedModule.Dependency: Equatable { + public static func == (lhs: ResolvedModule.Dependency, rhs: ResolvedModule.Dependency) -> Bool { switch (lhs, rhs) { case (.target(let lhsTarget, _), .target(let rhsTarget, _)): return lhsTarget.id == rhsTarget.id @@ -228,7 +234,7 @@ extension ResolvedTarget.Dependency: Equatable { } } -extension ResolvedTarget.Dependency: Hashable { +extension ResolvedModule.Dependency: Hashable { public func hash(into hasher: inout Hasher) { switch self { case .target(let target, _): @@ -239,7 +245,7 @@ extension ResolvedTarget.Dependency: Hashable { } } -extension ResolvedTarget: Identifiable { +extension ResolvedModule: Identifiable { /// Resolved target identity that uniquely identifies it in a resolution graph. public struct ID: Hashable { public let targetName: String diff --git a/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift b/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift index d3c736841e5..f8281b25146 100644 --- a/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift +++ b/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift @@ -94,7 +94,7 @@ public protocol BuildPlan { extension BuildPlan { /// Parameters used for building a given target. - public func buildParameters(for target: ResolvedTarget) -> BuildParameters { + public func buildParameters(for target: ResolvedModule) -> BuildParameters { switch target.buildTriple { case .tools: return self.toolsBuildParameters diff --git a/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift b/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift index 83852545d3e..4352b35ffac 100644 --- a/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift +++ b/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift @@ -28,7 +28,7 @@ internal struct PluginContextSerializer { var paths: [WireInput.URL] = [] var pathsToIds: [AbsolutePath: WireInput.URL.Id] = [:] var targets: [WireInput.Target] = [] - var targetsToWireIDs: [ResolvedTarget.ID: WireInput.Target.Id] = [:] + var targetsToWireIDs: [ResolvedModule.ID: WireInput.Target.Id] = [:] var products: [WireInput.Product] = [] var productsToWireIDs: [ResolvedProduct.ID: WireInput.Product.Id] = [:] var packages: [WireInput.Package] = [] diff --git a/Sources/SPMBuildCore/Plugins/PluginInvocation.swift b/Sources/SPMBuildCore/Plugins/PluginInvocation.swift index 435770e804a..9fb03674540 100644 --- a/Sources/SPMBuildCore/Plugins/PluginInvocation.swift +++ b/Sources/SPMBuildCore/Plugins/PluginInvocation.swift @@ -21,7 +21,7 @@ import protocol TSCBasic.DiagnosticLocation public enum PluginAction { case createBuildToolCommands( package: ResolvedPackage, - target: ResolvedTarget, + target: ResolvedModule, pluginGeneratedSources: [AbsolutePath], pluginGeneratedResources: [AbsolutePath] ) diff --git a/Sources/SPMBuildCore/ResolvedPackage+Extensions.swift b/Sources/SPMBuildCore/ResolvedPackage+Extensions.swift index 791299fe0bc..f7fb52c75c4 100644 --- a/Sources/SPMBuildCore/ResolvedPackage+Extensions.swift +++ b/Sources/SPMBuildCore/ResolvedPackage+Extensions.swift @@ -11,10 +11,10 @@ //===----------------------------------------------------------------------===// import struct PackageGraph.ResolvedPackage -import struct PackageGraph.ResolvedTarget +import struct PackageGraph.ResolvedModule extension ResolvedPackage { - package func packageNameArgument(target: ResolvedTarget, isPackageNameSupported: Bool) -> [String] { + package func packageNameArgument(target: ResolvedModule, isPackageNameSupported: Bool) -> [String] { if self.manifest.usePackageNameFlag, target.packageAccess { ["-package-name", self.identity.description.spm_mangledToC99ExtendedIdentifier()] } else { diff --git a/Sources/SourceKitLSPAPI/BuildDescription.swift b/Sources/SourceKitLSPAPI/BuildDescription.swift index d9287580bae..5cdec76454f 100644 --- a/Sources/SourceKitLSPAPI/BuildDescription.swift +++ b/Sources/SourceKitLSPAPI/BuildDescription.swift @@ -21,7 +21,7 @@ private import SPMBuildCore import class Build.BuildPlan import class Build.ClangTargetBuildDescription import class Build.SwiftTargetBuildDescription -import struct PackageGraph.ResolvedTarget +import struct PackageGraph.ResolvedModule public protocol BuildTarget { var sources: [URL] { get } @@ -71,7 +71,7 @@ public struct BuildDescription { } // FIXME: should not use `ResolvedTarget` in the public interface - public func getBuildTarget(for target: ResolvedTarget) -> BuildTarget? { + public func getBuildTarget(for target: ResolvedModule) -> BuildTarget? { if let description = buildPlan.targetMap[target.id] { switch description { case .clang(let description): diff --git a/Sources/SourceKitLSPAPI/PluginTargetBuildDescription.swift b/Sources/SourceKitLSPAPI/PluginTargetBuildDescription.swift index 8524908f5c1..9fc7403baba 100644 --- a/Sources/SourceKitLSPAPI/PluginTargetBuildDescription.swift +++ b/Sources/SourceKitLSPAPI/PluginTargetBuildDescription.swift @@ -12,17 +12,17 @@ import struct Foundation.URL -import struct PackageGraph.ResolvedTarget +import struct PackageGraph.ResolvedModule private import class PackageLoading.ManifestLoader internal import struct PackageModel.ToolsVersion private import class PackageModel.UserToolchain struct PluginTargetBuildDescription: BuildTarget { - private let target: ResolvedTarget + private let target: ResolvedModule private let toolsVersion: ToolsVersion - init(target: ResolvedTarget, toolsVersion: ToolsVersion) { + init(target: ResolvedModule, toolsVersion: ToolsVersion) { assert(target.type == .plugin) self.target = target self.toolsVersion = toolsVersion diff --git a/Sources/XCBuildSupport/PIFBuilder.swift b/Sources/XCBuildSupport/PIFBuilder.swift index dfa8937abaf..986f9058085 100644 --- a/Sources/XCBuildSupport/PIFBuilder.swift +++ b/Sources/XCBuildSupport/PIFBuilder.swift @@ -241,7 +241,7 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { private let fileSystem: FileSystem private let observabilityScope: ObservabilityScope private var binaryGroup: PIFGroupBuilder! - private let executableTargetProductMap: [ResolvedTarget.ID: ResolvedProduct] + private let executableTargetProductMap: [ResolvedModule.ID: ResolvedProduct] var isRootPackage: Bool { package.manifest.packageKind.isRoot } @@ -372,7 +372,7 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { } } - private func addTarget(for target: ResolvedTarget) throws { + private func addTarget(for target: ResolvedModule) throws { switch target.type { case .library: try self.addLibraryTarget(for: target) @@ -591,7 +591,7 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { pifTarget.addBuildConfiguration(name: "Release", settings: settings) } - private func addLibraryTarget(for target: ResolvedTarget) throws { + private func addLibraryTarget(for target: ResolvedModule) throws { let pifTarget = addTarget( guid: target.pifTargetGUID, name: target.name, @@ -720,7 +720,7 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { pifTarget.impartedBuildSettings = impartedSettings } - private func addSystemTarget(for target: ResolvedTarget) throws { + private func addSystemTarget(for target: ResolvedModule) throws { guard let systemTarget = target.underlying as? SystemLibraryTarget else { throw InternalError("unexpected target type") } @@ -774,7 +774,7 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { } private func addDependency( - to dependency: ResolvedTarget.Dependency, + to dependency: ResolvedModule.Dependency, in pifTarget: PIFTargetBuilder, linkProduct: Bool ) { @@ -797,7 +797,7 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { } private func addDependency( - to target: ResolvedTarget, + to target: ResolvedModule, in pifTarget: PIFTargetBuilder, conditions: [PackageCondition], linkProduct: Bool @@ -831,7 +831,7 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { ) } - private func addResourceBundle(for target: ResolvedTarget, in pifTarget: PIFTargetBuilder) -> String? { + private func addResourceBundle(for target: ResolvedModule, in pifTarget: PIFTargetBuilder) -> String? { guard !target.underlying.resources.isEmpty else { return nil } @@ -1395,7 +1395,7 @@ extension ResolvedPackage { extension ResolvedProduct { var pifTargetGUID: PIF.GUID { "PACKAGE-PRODUCT:\(name)" } - var mainTarget: ResolvedTarget { + var mainTarget: ResolvedModule { targets.first { $0.type == underlying.type.targetType }! } @@ -1403,23 +1403,23 @@ extension ResolvedProduct { /// based on their conditions and in a stable order. /// - Parameters: /// - environment: The build environment to use to filter dependencies on. - public func recursivePackageDependencies() -> [ResolvedTarget.Dependency] { - let initialDependencies = targets.map { ResolvedTarget.Dependency.target($0, conditions: []) } + public func recursivePackageDependencies() -> [ResolvedModule.Dependency] { + let initialDependencies = targets.map { ResolvedModule.Dependency.target($0, conditions: []) } return try! topologicalSort(initialDependencies) { dependency in return dependency.packageDependencies }.sorted() } } -extension ResolvedTarget { +extension ResolvedModule { var pifTargetGUID: PIF.GUID { "PACKAGE-TARGET:\(name)" } var pifResourceTargetGUID: PIF.GUID { "PACKAGE-RESOURCE:\(name)" } } -extension Array where Element == ResolvedTarget.Dependency { +extension Array where Element == ResolvedModule.Dependency { /// Sorts to get products first, sorted by name, followed by targets, sorted by name. - func sorted() -> [ResolvedTarget.Dependency] { + func sorted() -> [ResolvedModule.Dependency] { sorted { lhsDependency, rhsDependency in switch (lhsDependency, rhsDependency) { case (.product, .target): @@ -1441,7 +1441,7 @@ extension ResolvedPackage { } } -extension ResolvedTarget { +extension ResolvedModule { func deploymentTarget(for platform: PackageModel.Platform, usingXCTest: Bool = false) -> String? { return self.getSupportedPlatform(for: platform, usingXCTest: usingXCTest).version.versionString } @@ -1687,7 +1687,7 @@ extension PIF.PlatformFilter { private extension PIF.BuildSettings { mutating func addCommonSwiftSettings( package: ResolvedPackage, - target: ResolvedTarget, + target: ResolvedModule, parameters: PIFBuilderParameters ) { let packageOptions = package.packageNameArgument( From f249153af97fe1d85fd2cec2f2908e46864e245b Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Mon, 15 Apr 2024 20:52:49 +0100 Subject: [PATCH 084/159] Enable strict concurrency checks on `Basics` module (#7451) Also refactored `URLSessionHTTPClient` to remove `weak` references, which were incompatible with strict concurrency checks. --- Package.swift | 8 +- Sources/Basics/Archiver/Archiver.swift | 20 +- Sources/Basics/Archiver/TarArchiver.swift | 6 +- .../Basics/Archiver/UniversalArchiver.swift | 6 +- Sources/Basics/Archiver/ZipArchiver.swift | 6 +- Sources/Basics/Cancellator.swift | 6 +- .../Concurrency/ConcurrencyHelpers.swift | 2 +- Sources/Basics/Concurrency/TokenBucket.swift | 2 +- .../FileSystem/FileSystem+Extensions.swift | 2 +- Sources/Basics/FileSystem/TemporaryFile.swift | 2 +- .../Basics/HTTPClient/LegacyHTTPClient.swift | 2 +- .../HTTPClient/URLSessionHTTPClient.swift | 199 +++++------------- Sources/PackageRegistry/RegistryClient.swift | 6 +- .../RegistryDownloadsManager.swift | 2 +- 14 files changed, 88 insertions(+), 181 deletions(-) diff --git a/Package.swift b/Package.swift index ab065fca776..95a74ccc320 100644 --- a/Package.swift +++ b/Package.swift @@ -81,7 +81,6 @@ let systemSQLitePkgConfig: String? = "sqlite3" */ let autoProducts = [swiftPMProduct, swiftPMDataModelProduct] - let packageModelResourcesSettings: [SwiftSetting] let packageModelResources: [Resource] if ProcessInfo.processInfo.environment["SWIFTPM_USE_LIBRARIES_METADATA"] == nil { @@ -193,7 +192,10 @@ let package = Package( .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"), .product(name: "SystemPackage", package: "swift-system"), ], - exclude: ["CMakeLists.txt", "Vendor/README.md"] + exclude: ["CMakeLists.txt", "Vendor/README.md"], + swiftSettings: [ + .enableExperimentalFeature("StrictConcurrency"), + ] ), .target( @@ -701,7 +703,7 @@ package.targets.append(contentsOf: [ name: "FunctionalPerformanceTests", dependencies: [ "swift-package-manager", - "SPMTestSupport" + "SPMTestSupport", ] ), ]) diff --git a/Sources/Basics/Archiver/Archiver.swift b/Sources/Basics/Archiver/Archiver.swift index b76130b1500..e9d416ef21d 100644 --- a/Sources/Basics/Archiver/Archiver.swift +++ b/Sources/Basics/Archiver/Archiver.swift @@ -13,7 +13,7 @@ import _Concurrency /// The `Archiver` protocol abstracts away the different operations surrounding archives. -public protocol Archiver { +public protocol Archiver: Sendable { /// A set of extensions the current archiver supports. var supportedExtensions: Set { get } @@ -27,7 +27,7 @@ public protocol Archiver { func extract( from archivePath: AbsolutePath, to destinationPath: AbsolutePath, - completion: @escaping (Result) -> Void + completion: @escaping @Sendable (Result) -> Void ) /// Asynchronously compress the contents of a directory to a destination archive. @@ -40,7 +40,7 @@ public protocol Archiver { func compress( directory: AbsolutePath, to destinationPath: AbsolutePath, - completion: @escaping (Result) -> Void + completion: @escaping @Sendable (Result) -> Void ) /// Asynchronously validates if a file is an archive. @@ -51,7 +51,7 @@ public protocol Archiver { @available(*, noasync, message: "Use the async alternative") func validate( path: AbsolutePath, - completion: @escaping (Result) -> Void + completion: @escaping @Sendable (Result) -> Void ) } @@ -65,8 +65,8 @@ extension Archiver { from archivePath: AbsolutePath, to destinationPath: AbsolutePath ) async throws { - try await withCheckedThrowingContinuation { - self.extract(from: archivePath, to: destinationPath, completion: $0.resume(with:)) + try await withCheckedThrowingContinuation { continuation in + self.extract(from: archivePath, to: destinationPath, completion: { continuation.resume(with: $0) }) } } @@ -79,8 +79,8 @@ extension Archiver { directory: AbsolutePath, to destinationPath: AbsolutePath ) async throws { - try await withCheckedThrowingContinuation { - self.compress(directory: directory, to: destinationPath, completion: $0.resume(with:)) + try await withCheckedThrowingContinuation { continuation in + self.compress(directory: directory, to: destinationPath, completion: { continuation.resume(with: $0) }) } } @@ -91,8 +91,8 @@ extension Archiver { public func validate( path: AbsolutePath ) async throws -> Bool { - try await withCheckedThrowingContinuation { - self.validate(path: path, completion: $0.resume(with:)) + try await withCheckedThrowingContinuation { continuation in + self.validate(path: path, completion: { continuation.resume(with: $0) }) } } } diff --git a/Sources/Basics/Archiver/TarArchiver.swift b/Sources/Basics/Archiver/TarArchiver.swift index d1da7ed0c15..99948bf82a9 100644 --- a/Sources/Basics/Archiver/TarArchiver.swift +++ b/Sources/Basics/Archiver/TarArchiver.swift @@ -47,7 +47,7 @@ public struct TarArchiver: Archiver { public func extract( from archivePath: AbsolutePath, to destinationPath: AbsolutePath, - completion: @escaping (Result) -> Void + completion: @escaping @Sendable (Result) -> Void ) { do { guard self.fileSystem.exists(archivePath) else { @@ -84,7 +84,7 @@ public struct TarArchiver: Archiver { public func compress( directory: AbsolutePath, to destinationPath: AbsolutePath, - completion: @escaping (Result) -> Void + completion: @escaping @Sendable (Result) -> Void ) { do { guard self.fileSystem.isDirectory(directory) else { @@ -115,7 +115,7 @@ public struct TarArchiver: Archiver { } } - public func validate(path: AbsolutePath, completion: @escaping (Result) -> Void) { + public func validate(path: AbsolutePath, completion: @escaping @Sendable (Result) -> Void) { do { guard self.fileSystem.exists(path) else { throw FileSystemError(.noEntry, path.underlying) diff --git a/Sources/Basics/Archiver/UniversalArchiver.swift b/Sources/Basics/Archiver/UniversalArchiver.swift index cbd5d5d742f..d6ed496df97 100644 --- a/Sources/Basics/Archiver/UniversalArchiver.swift +++ b/Sources/Basics/Archiver/UniversalArchiver.swift @@ -73,7 +73,7 @@ public struct UniversalArchiver: Archiver { public func extract( from archivePath: AbsolutePath, to destinationPath: AbsolutePath, - completion: @escaping (Result) -> Void + completion: @escaping @Sendable (Result) -> Void ) { do { let archiver = try archiver(for: archivePath) @@ -86,7 +86,7 @@ public struct UniversalArchiver: Archiver { public func compress( directory: AbsolutePath, to destinationPath: AbsolutePath, - completion: @escaping (Result) -> Void + completion: @escaping @Sendable (Result) -> Void ) { do { let archiver = try archiver(for: destinationPath) @@ -98,7 +98,7 @@ public struct UniversalArchiver: Archiver { public func validate( path: AbsolutePath, - completion: @escaping (Result) -> Void + completion: @escaping @Sendable (Result) -> Void ) { do { let archiver = try archiver(for: path) diff --git a/Sources/Basics/Archiver/ZipArchiver.swift b/Sources/Basics/Archiver/ZipArchiver.swift index 9aab24e13ce..092752d8382 100644 --- a/Sources/Basics/Archiver/ZipArchiver.swift +++ b/Sources/Basics/Archiver/ZipArchiver.swift @@ -37,7 +37,7 @@ public struct ZipArchiver: Archiver, Cancellable { public func extract( from archivePath: AbsolutePath, to destinationPath: AbsolutePath, - completion: @escaping (Result) -> Void + completion: @escaping @Sendable (Result) -> Void ) { do { guard self.fileSystem.exists(archivePath) else { @@ -77,7 +77,7 @@ public struct ZipArchiver: Archiver, Cancellable { public func compress( directory: AbsolutePath, to destinationPath: AbsolutePath, - completion: @escaping (Result) -> Void + completion: @escaping @Sendable (Result) -> Void ) { do { guard self.fileSystem.isDirectory(directory) else { @@ -125,7 +125,7 @@ public struct ZipArchiver: Archiver, Cancellable { } } - public func validate(path: AbsolutePath, completion: @escaping (Result) -> Void) { + public func validate(path: AbsolutePath, completion: @escaping @Sendable (Result) -> Void) { do { guard self.fileSystem.exists(path) else { throw FileSystemError(.noEntry, path.underlying) diff --git a/Sources/Basics/Cancellator.swift b/Sources/Basics/Cancellator.swift index d2c808e88ef..e1e8f8d0434 100644 --- a/Sources/Basics/Cancellator.swift +++ b/Sources/Basics/Cancellator.swift @@ -18,9 +18,9 @@ import class TSCBasic.Thread import WinSDK #endif -public typealias CancellationHandler = (DispatchTime) throws -> Void +public typealias CancellationHandler = @Sendable (DispatchTime) throws -> Void -public final class Cancellator: Cancellable { +public final class Cancellator: Cancellable, Sendable { public typealias RegistrationKey = String private let observabilityScope: ObservabilityScope? @@ -119,7 +119,7 @@ public final class Cancellator: Cancellable { } @discardableResult - public func register(name: String, handler: @escaping () throws -> Void) -> RegistrationKey? { + public func register(name: String, handler: @escaping @Sendable () throws -> Void) -> RegistrationKey? { self.register(name: name, handler: { _ in try handler() }) } diff --git a/Sources/Basics/Concurrency/ConcurrencyHelpers.swift b/Sources/Basics/Concurrency/ConcurrencyHelpers.swift index 3a10d61a81c..7927d2e2dec 100644 --- a/Sources/Basics/Concurrency/ConcurrencyHelpers.swift +++ b/Sources/Basics/Concurrency/ConcurrencyHelpers.swift @@ -64,7 +64,7 @@ public func safe_async( } /// Bridges between potentially blocking methods that take a result completion closure and async/await -public func safe_async(_ body: @escaping (@escaping (Result) -> Void) -> Void) async -> T { +public func safe_async(_ body: @escaping @Sendable (@escaping (Result) -> Void) -> Void) async -> T { await withCheckedContinuation { continuation in // It is possible that body make block indefinitely on a lock, semaphore, // or similar then synchronously call the completion handler. For full safety diff --git a/Sources/Basics/Concurrency/TokenBucket.swift b/Sources/Basics/Concurrency/TokenBucket.swift index e9cf6ff4251..010da630a35 100644 --- a/Sources/Basics/Concurrency/TokenBucket.swift +++ b/Sources/Basics/Concurrency/TokenBucket.swift @@ -30,7 +30,7 @@ public actor TokenBucket { /// invocations of `withToken` will suspend until a "free" token is available. /// - Parameter body: The closure to invoke when a token is available. /// - Returns: Resulting value returned by `body`. - public func withToken( + public func withToken( _ body: @Sendable () async throws -> ReturnType ) async rethrows -> ReturnType { await self.getToken() diff --git a/Sources/Basics/FileSystem/FileSystem+Extensions.swift b/Sources/Basics/FileSystem/FileSystem+Extensions.swift index ac0c0ace718..f31604263e7 100644 --- a/Sources/Basics/FileSystem/FileSystem+Extensions.swift +++ b/Sources/Basics/FileSystem/FileSystem+Extensions.swift @@ -25,7 +25,7 @@ import var TSCBasic.localFileSystem import protocol TSCBasic.WritableByteStream public typealias FileSystem = TSCBasic.FileSystem -public var localFileSystem = TSCBasic.localFileSystem +public let localFileSystem = TSCBasic.localFileSystem // MARK: - Custom path diff --git a/Sources/Basics/FileSystem/TemporaryFile.swift b/Sources/Basics/FileSystem/TemporaryFile.swift index 756f5022f4a..a9f253b6f08 100644 --- a/Sources/Basics/FileSystem/TemporaryFile.swift +++ b/Sources/Basics/FileSystem/TemporaryFile.swift @@ -72,7 +72,7 @@ public func withTemporaryDirectory( dir: AbsolutePath? = nil, prefix: String = "TemporaryDirectory", removeTreeOnDeinit: Bool = false, - _ body: @escaping (AbsolutePath) async throws -> Result + _ body: @escaping @Sendable (AbsolutePath) async throws -> Result ) throws -> Task { try withTemporaryDirectory(fileSystem: fileSystem, dir: dir, prefix: prefix) { path, cleanup in defer { if removeTreeOnDeinit { cleanup(path) } } diff --git a/Sources/Basics/HTTPClient/LegacyHTTPClient.swift b/Sources/Basics/HTTPClient/LegacyHTTPClient.swift index b5294ccfabf..524459e54a9 100644 --- a/Sources/Basics/HTTPClient/LegacyHTTPClient.swift +++ b/Sources/Basics/HTTPClient/LegacyHTTPClient.swift @@ -25,7 +25,7 @@ public final class LegacyHTTPClient: Cancellable { public typealias Configuration = LegacyHTTPClientConfiguration public typealias Request = LegacyHTTPClientRequest public typealias Response = HTTPClientResponse - public typealias Handler = (Request, ProgressHandler?, @escaping (Result) -> Void) -> Void + public typealias Handler = (Request, ProgressHandler?, @escaping @Sendable (Result) -> Void) -> Void public typealias ProgressHandler = @Sendable (_ bytesReceived: Int64, _ totalBytes: Int64?) throws -> Void public typealias CompletionHandler = @Sendable (Result) -> Void diff --git a/Sources/Basics/HTTPClient/URLSessionHTTPClient.swift b/Sources/Basics/HTTPClient/URLSessionHTTPClient.swift index b706b173d39..7c27608749d 100644 --- a/Sources/Basics/HTTPClient/URLSessionHTTPClient.swift +++ b/Sources/Basics/HTTPClient/URLSessionHTTPClient.swift @@ -14,18 +14,41 @@ import _Concurrency import Foundation import struct TSCUtility.Versioning #if canImport(FoundationNetworking) -// FIXME: this brings OpenSSL dependency on Linux -// need to decide how to best deal with that +// FIXME: this brings OpenSSL dependency on Linux and needs to be replaced with `swift-server/async-http-client` package import FoundationNetworking #endif final class URLSessionHTTPClient: Sendable { + private let dataSession: URLSession + private let downloadSession: URLSession private let dataTaskManager: DataTaskManager private let downloadTaskManager: DownloadTaskManager init(configuration: URLSessionConfiguration = .default) { - self.dataTaskManager = DataTaskManager(configuration: configuration) - self.downloadTaskManager = DownloadTaskManager(configuration: configuration) + let dataDelegateQueue = OperationQueue() + dataDelegateQueue.name = "org.swift.swiftpm.urlsession-http-client-data-delegate" + dataDelegateQueue.maxConcurrentOperationCount = 1 + self.dataTaskManager = DataTaskManager() + self.dataSession = URLSession( + configuration: configuration, + delegate: self.dataTaskManager, + delegateQueue: dataDelegateQueue + ) + + let downloadDelegateQueue = OperationQueue() + downloadDelegateQueue.name = "org.swift.swiftpm.urlsession-http-client-download-delegate" + downloadDelegateQueue.maxConcurrentOperationCount = 1 + self.downloadTaskManager = DownloadTaskManager() + self.downloadSession = URLSession( + configuration: configuration, + delegate: self.downloadTaskManager, + delegateQueue: downloadDelegateQueue + ) + } + + deinit { + dataSession.finishTasksAndInvalidate() + downloadSession.finishTasksAndInvalidate() } @Sendable @@ -38,22 +61,28 @@ final class URLSessionHTTPClient: Sendable { let task: URLSessionTask switch request.kind { case .generic: - task = self.dataTaskManager.makeTask( + let dataTask = self.dataSession.dataTask(with: urlRequest) + self.dataTaskManager.register( + task: dataTask, urlRequest: urlRequest, authorizationProvider: request.options.authorizationProvider, progress: progress, completion: { continuation.resume(with: $0) } ) + task = dataTask case .download(_, let destination): - task = self.downloadTaskManager.makeTask( + let downloadTask = self.downloadSession.downloadTask(with: urlRequest) + self.downloadTaskManager.register( + task: downloadTask, urlRequest: urlRequest, - // FIXME: always using a synchronous filesystem, because `URLSessionDownloadDelegate` + // FIXME: always using synchronous filesystem, because `URLSessionDownloadDelegate` // needs temporary files to moved out of temporary locations synchronously in delegate callbacks. fileSystem: localFileSystem, destination: destination, progress: progress, completion: { continuation.resume(with: $0) } ) + task = downloadTask } task.resume() } @@ -69,101 +98,41 @@ final class URLSessionHTTPClient: Sendable { let task: URLSessionTask switch request.kind { case .generic: - task = self.dataTaskManager.makeTask( + let dataTask = self.dataSession.dataTask(with: urlRequest) + self.dataTaskManager.register( + task: dataTask, urlRequest: urlRequest, authorizationProvider: request.options.authorizationProvider, progress: progress, completion: completion ) + task = dataTask case .download(let fileSystem, let destination): - task = self.downloadTaskManager.makeTask( + let downloadTask = self.downloadSession.downloadTask(with: urlRequest) + self.downloadTaskManager.register( + task: downloadTask, urlRequest: urlRequest, fileSystem: fileSystem, destination: destination, progress: progress, completion: completion ) + task = downloadTask } task.resume() } } -/// A weak wrapper around `DataTaskManager` that conforms to `URLSessionDataDelegate`. -/// -/// This ensures that we don't get a retain cycle between `DataTaskManager.session` -> `URLSession.delegate` -> `DataTaskManager`. -/// -/// The `DataTaskManager` is being kept alive by a reference from all `DataTask`s that it manages. Once all the -/// `DataTasks` have finished and are deallocated, `DataTaskManager` will get deinitialized, which invalidates the -/// session, which then lets go of `WeakDataTaskManager`. -private class WeakDataTaskManager: NSObject, URLSessionDataDelegate { - private weak var dataTaskManager: DataTaskManager? - - init(_ dataTaskManager: DataTaskManager? = nil) { - self.dataTaskManager = dataTaskManager - } - - func urlSession( - _ session: URLSession, - dataTask: URLSessionDataTask, - didReceive response: URLResponse, - completionHandler: @escaping (URLSession.ResponseDisposition) -> Void - ) { - dataTaskManager?.urlSession( - session, - dataTask: dataTask, - didReceive: response, - completionHandler: completionHandler - ) - } - - func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { - dataTaskManager?.urlSession(session, dataTask: dataTask, didReceive: data) - } - - func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { - dataTaskManager?.urlSession(session, task: task, didCompleteWithError: error) - } - - func urlSession( - _ session: URLSession, - task: URLSessionTask, - willPerformHTTPRedirection response: HTTPURLResponse, - newRequest request: URLRequest, - completionHandler: @escaping (URLRequest?) -> Void - ) { - dataTaskManager?.urlSession( - session, - task: task, - willPerformHTTPRedirection: response, - newRequest: request, - completionHandler: completionHandler - ) - } -} - -private final class DataTaskManager: @unchecked Sendable { +private final class DataTaskManager: NSObject, URLSessionDataDelegate { private let tasks = ThreadSafeKeyValueStore() - private let delegateQueue: OperationQueue - private var session: URLSession! - - public init(configuration: URLSessionConfiguration) { - self.delegateQueue = OperationQueue() - self.delegateQueue.name = "org.swift.swiftpm.urlsession-http-client-data-delegate" - self.delegateQueue.maxConcurrentOperationCount = 1 - self.session = URLSession(configuration: configuration, delegate: WeakDataTaskManager(self), delegateQueue: self.delegateQueue) - } - deinit { - session.finishTasksAndInvalidate() - } - - func makeTask( + func register( + task: URLSessionDataTask, urlRequest: URLRequest, authorizationProvider: LegacyHTTPClientConfiguration.AuthorizationProvider?, progress: LegacyHTTPClient.ProgressHandler?, completion: @escaping LegacyHTTPClient.CompletionHandler - ) -> URLSessionDataTask { - let task = self.session.dataTask(with: urlRequest) + ) { self.tasks[task.taskIdentifier] = DataTask( task: task, progressHandler: progress, @@ -171,7 +140,6 @@ private final class DataTaskManager: @unchecked Sendable { completionHandler: completion, authorizationProvider: authorizationProvider ) - return task } public func urlSession( @@ -281,73 +249,17 @@ private final class DataTaskManager: @unchecked Sendable { } } -/// This uses the same pattern as `WeakDataTaskManager`. See comment on that type. -private class WeakDownloadTaskManager: NSObject, URLSessionDownloadDelegate { - private weak var downloadTaskManager: DownloadTaskManager? - - init(_ downloadTaskManager: DownloadTaskManager? = nil) { - self.downloadTaskManager = downloadTaskManager - } - - func urlSession( - _ session: URLSession, - downloadTask: URLSessionDownloadTask, - didWriteData bytesWritten: Int64, - totalBytesWritten: Int64, - totalBytesExpectedToWrite: Int64 - ) { - downloadTaskManager?.urlSession( - session, - downloadTask: downloadTask, - didWriteData: bytesWritten, - totalBytesWritten: totalBytesWritten, - totalBytesExpectedToWrite: totalBytesExpectedToWrite - ) - } - - func urlSession( - _ session: URLSession, - downloadTask: URLSessionDownloadTask, - didFinishDownloadingTo location: URL - ) { - downloadTaskManager?.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: location) - } - - func urlSession( - _ session: URLSession, - task downloadTask: URLSessionTask, - didCompleteWithError error: Error? - ) { - downloadTaskManager?.urlSession(session, task: downloadTask, didCompleteWithError: error) - } -} - -private final class DownloadTaskManager: @unchecked Sendable { +private final class DownloadTaskManager: NSObject, URLSessionDownloadDelegate { private let tasks = ThreadSafeKeyValueStore() - private let delegateQueue: OperationQueue - // FIXME: can't be `let` instead of `var`, as `URLSession` holds a reference to `self`. - private var session: URLSession! - - init(configuration: URLSessionConfiguration) { - self.delegateQueue = OperationQueue() - self.delegateQueue.name = "org.swift.swiftpm.urlsession-http-client-download-delegate" - self.delegateQueue.maxConcurrentOperationCount = 1 - self.session = URLSession(configuration: configuration, delegate: WeakDownloadTaskManager(self), delegateQueue: self.delegateQueue) - } - - deinit { - session.finishTasksAndInvalidate() - } - - func makeTask( + func register( + task: URLSessionDownloadTask, urlRequest: URLRequest, fileSystem: FileSystem, destination: AbsolutePath, progress: LegacyHTTPClient.ProgressHandler?, completion: @escaping LegacyHTTPClient.CompletionHandler - ) -> URLSessionDownloadTask { - let task = self.session.downloadTask(with: urlRequest) + ) { self.tasks[task.taskIdentifier] = DownloadTask( task: task, fileSystem: fileSystem, @@ -356,7 +268,6 @@ private final class DownloadTaskManager: @unchecked Sendable { progressHandler: progress, completionHandler: completion ) - return task } func urlSession( @@ -430,11 +341,6 @@ private final class DownloadTaskManager: @unchecked Sendable { let task: URLSessionDownloadTask let fileSystem: FileSystem let destination: AbsolutePath - /// A strong reference to keep the `DownloadTaskManager` alive so it can handle the callbacks from the - /// `URLSession`. - /// - /// See comment on `WeakDownloadTaskManager`. - private let downloadTaskManager: DownloadTaskManager let progressHandler: LegacyHTTPClient.ProgressHandler? let completionHandler: LegacyHTTPClient.CompletionHandler @@ -451,7 +357,6 @@ private final class DownloadTaskManager: @unchecked Sendable { self.task = task self.fileSystem = fileSystem self.destination = destination - self.downloadTaskManager = downloadTaskManager self.progressHandler = progressHandler self.completionHandler = completionHandler } diff --git a/Sources/PackageRegistry/RegistryClient.swift b/Sources/PackageRegistry/RegistryClient.swift index a13fc4cd8f6..96445280282 100644 --- a/Sources/PackageRegistry/RegistryClient.swift +++ b/Sources/PackageRegistry/RegistryClient.swift @@ -983,7 +983,7 @@ public final class RegistryClient: Cancellable { package: PackageIdentity, version: Version, destinationPath: AbsolutePath, - progressHandler: ((_ bytesReceived: Int64, _ totalBytes: Int64?) -> Void)?, + progressHandler: (@Sendable (_ bytesReceived: Int64, _ totalBytes: Int64?) -> Void)?, timeout: DispatchTimeInterval? = .none, fileSystem: FileSystem, observabilityScope: ObservabilityScope, @@ -1009,7 +1009,7 @@ public final class RegistryClient: Cancellable { package: PackageIdentity, version: Version, destinationPath: AbsolutePath, - progressHandler: ((_ bytesReceived: Int64, _ totalBytes: Int64?) -> Void)?, + progressHandler: (@Sendable (_ bytesReceived: Int64, _ totalBytes: Int64?) -> Void)?, timeout: DispatchTimeInterval? = .none, fileSystem: FileSystem, observabilityScope: ObservabilityScope, @@ -1063,7 +1063,7 @@ public final class RegistryClient: Cancellable { package: PackageIdentity.RegistryIdentity, version: Version, destinationPath: AbsolutePath, - progressHandler: ((_ bytesReceived: Int64, _ totalBytes: Int64?) -> Void)?, + progressHandler: (@Sendable (_ bytesReceived: Int64, _ totalBytes: Int64?) -> Void)?, timeout: DispatchTimeInterval?, fileSystem: FileSystem, observabilityScope: ObservabilityScope, diff --git a/Sources/PackageRegistry/RegistryDownloadsManager.swift b/Sources/PackageRegistry/RegistryDownloadsManager.swift index 3c24234bfef..12462ff744b 100644 --- a/Sources/PackageRegistry/RegistryDownloadsManager.swift +++ b/Sources/PackageRegistry/RegistryDownloadsManager.swift @@ -163,7 +163,7 @@ public class RegistryDownloadsManager: Cancellable { observabilityScope: ObservabilityScope, delegateQueue: DispatchQueue, callbackQueue: DispatchQueue, - completion: @escaping (Result) -> Void + completion: @escaping @Sendable (Result) -> Void ) { if let cachePath { do { From d8c8d0f1478ce0164b7e69f98bf1a34068622453 Mon Sep 17 00:00:00 2001 From: Philipp Wallrich Date: Mon, 15 Apr 2024 23:06:37 +0200 Subject: [PATCH 085/159] Fix empty token on swift package-registry login (Issue #7453) (#7454) Fix empty token when adding a new package-registry and adding token interactively on release builds ### Motivation: When executing swift package-registry login it asks for a token but fails to retrieve it properly. It sends an empty string to the registry server and login therefore fails. (see https://github.com/apple/swift-package-manager/issues/7453) ### Modifications: I've debugged the code a bit and found out, that somehow `buffer` and `passwordPtr` don't seem to be holding correct values after the `readpassphrase` call. - An easy quick fix is to make sure that `buffer` doesn't get deallocated by adding smth like `_ = buffer` after the String init. This works but is not nice. - My first try was to use `buffer` instead of `passwordPtr` to create the string. These works, but I remember reading somewhere that `&` can be quite nasty sometimes. - I also tried `buffer.withUnsafeMutablePointer`. However `readpassphrase` seems to not only change the content of `buffer` but move it. This leads to a runtime failure `Fatal error: Array withUnsafeMutableBufferPointer: replacing the buffer is not allowed` ### Result: The buffer is retained, the token is properly parsed and sent to the server. --- .../PackageRegistryCommand+Auth.swift | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Sources/PackageRegistryCommand/PackageRegistryCommand+Auth.swift b/Sources/PackageRegistryCommand/PackageRegistryCommand+Auth.swift index 5d55e0c40ab..f129a011424 100644 --- a/Sources/PackageRegistryCommand/PackageRegistryCommand+Auth.swift +++ b/Sources/PackageRegistryCommand/PackageRegistryCommand+Auth.swift @@ -71,12 +71,13 @@ private func readpassword(_ prompt: String) throws -> String { #if canImport(Darwin) var buffer = [CChar](repeating: 0, count: PackageRegistryCommand.Login.passwordBufferSize) + password = try withExtendedLifetime(buffer) { + guard let passwordPtr = readpassphrase(prompt, &buffer, buffer.count, 0) else { + throw StringError("unable to read input") + } - guard let passwordPtr = readpassphrase(prompt, &buffer, buffer.count, 0) else { - throw StringError("unable to read input") + return String(cString: passwordPtr) } - - password = String(cString: passwordPtr) #else // GNU C implementation of getpass has no limit on the password length // (https://man7.org/linux/man-pages/man3/getpass.3.html) From d811fafe6290d710976cb5e6f999a004cbb9dd1c Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 16 Apr 2024 22:02:36 +0100 Subject: [PATCH 086/159] Add `UndirectedGraph` and `DirectedGraph` data structures (#7460) `DirectedGraph` can be used to represent the existing modules graph in the `PackageGraph` module hopefully with better performance due to reduced number of allocations. `UndirectedGraph` could represent a new linkage graph concept, which would help with breaking dependency cycles for certain packages with products that don't have a linkage cycle. I started with an adjacency list in a directed graph implementation, but then proceeded to an undirected graph where adjacency matrix seemed obviously more performant when adding edges: just flipping two bits in the matrix instead of adding potential array (re-)allocations with the list. I'm unsure though if either is a clear win in all cases, adjacency matrix may be fast at adding edges, but not the fastest for different kinds of graphs analysis and lookups. Keeping both implementations for future reference. --- Sources/Basics/CMakeLists.txt | 3 + Sources/Basics/Graph/AdjacencyMatrix.swift | 61 +++++++++++++++++ Sources/Basics/Graph/DirectedGraph.swift | 54 +++++++++++++++ Sources/Basics/Graph/UndirectedGraph.swift | 68 +++++++++++++++++++ .../Graph/AdjacencyMatrixTests.swift | 40 +++++++++++ .../Graph/DirectedGraphTests.swift | 31 +++++++++ .../Graph/UndirectedGraphTests.swift | 38 +++++++++++ 7 files changed, 295 insertions(+) create mode 100644 Sources/Basics/Graph/AdjacencyMatrix.swift create mode 100644 Sources/Basics/Graph/DirectedGraph.swift create mode 100644 Sources/Basics/Graph/UndirectedGraph.swift create mode 100644 Tests/BasicsTests/Graph/AdjacencyMatrixTests.swift create mode 100644 Tests/BasicsTests/Graph/DirectedGraphTests.swift create mode 100644 Tests/BasicsTests/Graph/UndirectedGraphTests.swift diff --git a/Sources/Basics/CMakeLists.txt b/Sources/Basics/CMakeLists.txt index bed9dcebfd2..bb3948ef432 100644 --- a/Sources/Basics/CMakeLists.txt +++ b/Sources/Basics/CMakeLists.txt @@ -34,6 +34,9 @@ add_library(Basics FileSystem/TemporaryFile.swift FileSystem/TSCAdapters.swift FileSystem/VFSOverlay.swift + Graph/AdjacencyMatrix.swift + Graph/DirectedGraph.swift + Graph/UndirectedGraph.swift SourceControlURL.swift HTTPClient/HTTPClient.swift HTTPClient/HTTPClientConfiguration.swift diff --git a/Sources/Basics/Graph/AdjacencyMatrix.swift b/Sources/Basics/Graph/AdjacencyMatrix.swift new file mode 100644 index 00000000000..9326d499a7c --- /dev/null +++ b/Sources/Basics/Graph/AdjacencyMatrix.swift @@ -0,0 +1,61 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +/// A matrix storing bits of `true`/`false` state for a given combination of row and column indices. Used as +/// a square matrix indicating edges in graphs, where rows and columns are indices in a storage of graph's nodes. +/// +/// For example, in a graph that contains 3 nodes `matrix[row: 1, column: 2]` evaluating to `true` means an edge +/// between nodes with indices `1` and `2` exists. `matrix[row: 1, column: 2]` evaluating to `false` means that no +/// edge exists. +/// +/// See https://en.wikipedia.org/wiki/Adjacency_matrix for more details. +struct AdjacencyMatrix { + let columns: Int + let rows: Int + private var bytes: [UInt8] + + /// Allocates a new bit matrix with a given size. + /// - Parameters: + /// - rows: Number of rows in the matrix. + /// - columns: Number of columns in the matrix. + init(rows: Int, columns: Int) { + self.columns = columns + self.rows = rows + + let (quotient, remainder) = (rows * columns).quotientAndRemainder(dividingBy: 8) + self.bytes = .init(repeating: 0, count: quotient + (remainder > 0 ? 1 : 0)) + } + + var bitCount: Int { + bytes.count * 8 + } + + private func calculateOffsets(row: Int, column: Int) -> (byteOffset: Int, bitOffsetInByte: Int) { + let totalBitOffset = row * columns + column + return (byteOffset: totalBitOffset / 8, bitOffsetInByte: totalBitOffset % 8) + } + + subscript(row: Int, column: Int) -> Bool { + get { + let (byteOffset, bitOffsetInByte) = calculateOffsets(row: row, column: column) + + let result = (self.bytes[byteOffset] >> bitOffsetInByte) & 1 + return result == 1 + } + + set { + let (byteOffset, bitOffsetInByte) = calculateOffsets(row: row, column: column) + + self.bytes[byteOffset] |= 1 << bitOffsetInByte + } + } +} diff --git a/Sources/Basics/Graph/DirectedGraph.swift b/Sources/Basics/Graph/DirectedGraph.swift new file mode 100644 index 00000000000..58867b99b9d --- /dev/null +++ b/Sources/Basics/Graph/DirectedGraph.swift @@ -0,0 +1,54 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import struct DequeModule.Deque + +/// Directed graph that stores edges in [adjacency lists](https://en.wikipedia.org/wiki/Adjacency_list). +struct DirectedGraph { + init(nodes: [Node]) { + self.nodes = nodes + self.edges = .init(repeating: [], count: nodes.count) + } + + private var nodes: [Node] + private var edges: [[Int]] + + mutating func addEdge(source: Int, destination: Int) { + self.edges[source].append(destination) + } + + /// Checks whether a path via previously created edges between two given nodes exists. + /// - Parameters: + /// - source: `Index` of a node to start traversing edges from. + /// - destination: `Index` of a node to which a path could exist via edges from `source`. + /// - Returns: `true` if a path from `source` to `destination` exists, `false` otherwise. + func areNodesConnected(source: Int, destination: Int) -> Bool { + var todo = Deque([source]) + var done = Set() + + while !todo.isEmpty { + let nodeIndex = todo.removeFirst() + + for reachableIndex in self.edges[nodeIndex] { + if reachableIndex == destination { + return true + } else if !done.contains(reachableIndex) { + todo.append(reachableIndex) + } + } + + done.insert(nodeIndex) + } + + return false + } +} diff --git a/Sources/Basics/Graph/UndirectedGraph.swift b/Sources/Basics/Graph/UndirectedGraph.swift new file mode 100644 index 00000000000..02a0bdca90c --- /dev/null +++ b/Sources/Basics/Graph/UndirectedGraph.swift @@ -0,0 +1,68 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import struct DequeModule.Deque + +/// Undirected graph that stores edges in an [adjacency matrix](https://en.wikipedia.org/wiki/Adjacency_list). +struct UndirectedGraph { + init(nodes: [Node]) { + self.nodes = nodes + self.edges = .init(rows: nodes.count, columns: nodes.count) + } + + private var nodes: [Node] + private var edges: AdjacencyMatrix + + mutating func addEdge(source: Int, destination: Int) { + // Adjacency matrix is symmetrical for undirected graphs. + self.edges[source, destination] = true + self.edges[destination, source] = true + } + + /// Checks whether a connection via previously created edges between two given nodes exists. + /// - Parameters: + /// - source: `Index` of a node to start traversing edges from. + /// - destination: `Index` of a node to which a connection could exist via edges from `source`. + /// - Returns: `true` if a path from `source` to `destination` exists, `false` otherwise. + func areNodesConnected(source: Int, destination: Int) -> Bool { + var todo = Deque([source]) + var done = Set() + + while !todo.isEmpty { + let nodeIndex = todo.removeFirst() + + for reachableIndex in self.edges.nodesAdjacentTo(nodeIndex) { + if reachableIndex == destination { + return true + } else if !done.contains(reachableIndex) { + todo.append(reachableIndex) + } + } + + done.insert(nodeIndex) + } + + return false + } +} + +private extension AdjacencyMatrix { + func nodesAdjacentTo(_ nodeIndex: Int) -> [Int] { + var result = [Int]() + + for i in 0.. Date: Wed, 17 Apr 2024 01:49:03 +0100 Subject: [PATCH 087/159] Remove outdated doc comment in `Workspace+State.swift` (#7463) This "Returns true if the state file exists on the filesystem" doc comment was copy-pasted from a function above and is clearly wrong: `reload()` function doesn't return any values. --- Sources/Workspace/Workspace+State.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Sources/Workspace/Workspace+State.swift b/Sources/Workspace/Workspace+State.swift index ec4ef688a55..0d70ee2af0c 100644 --- a/Sources/Workspace/Workspace+State.swift +++ b/Sources/Workspace/Workspace+State.swift @@ -76,7 +76,6 @@ public final class WorkspaceState { self.storage.fileExists() } - /// Returns true if the state file exists on the filesystem. func reload() throws { let storedState = try self.storage.load() self.dependencies = storedState.dependencies From 36a3339654738da06b1cb889b6a83f975ba70379 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Wed, 17 Apr 2024 15:09:29 +0100 Subject: [PATCH 088/159] Add basic thresholds for `PackageGraphBenchmarks.swift` (#7462) This records thresholds for two relatively stable metrics: total malloc count and total number of syscalls. We don't run these benchmarks on CI yet, but now it allows tracking regressions locally. --- .../PackageGraphBenchmarks.swift | 26 ++++++++++++++-- Benchmarks/README.md | 30 +++++++++++++++++++ ...aphBenchmarks.PackageGraphLoading.p90.json | 4 +++ 3 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 Benchmarks/README.md create mode 100644 Benchmarks/Thresholds/PackageGraphBenchmarks.PackageGraphLoading.p90.json diff --git a/Benchmarks/Benchmarks/PackageGraphBenchmarks/PackageGraphBenchmarks.swift b/Benchmarks/Benchmarks/PackageGraphBenchmarks/PackageGraphBenchmarks.swift index b35bd27bb9d..4846f30d80a 100644 --- a/Benchmarks/Benchmarks/PackageGraphBenchmarks/PackageGraphBenchmarks.swift +++ b/Benchmarks/Benchmarks/PackageGraphBenchmarks/PackageGraphBenchmarks.swift @@ -1,14 +1,34 @@ import Basics import Benchmark +import Foundation import PackageModel import Workspace let benchmarks = { + let defaultMetrics: [BenchmarkMetric] + if let envVar = ProcessInfo.processInfo.environment["SWIFTPM_BENCHMARK_ALL_METRICS"], + envVar.lowercased() == "true" || envVar == "1" { + defaultMetrics = .all + } else { + defaultMetrics = [ + .mallocCountTotal, + .syscalls, + ] + } + + // Benchmarks computation of a resolved graph of modules for a package using `Workspace` as an entry point. It runs PubGrub to get + // resolved concrete versions of dependencies, assigning all modules and products to each other as corresponding dependencies + // with their build triples, but with the build plan not yet constructed. In this benchmark specifically we're loading `Package.swift` + // for SwiftPM itself. Benchmark( - "Package graph loading", + "SwiftPMWorkspaceModulesGraph", configuration: .init( - metrics: BenchmarkMetric.all, - maxDuration: .seconds(10) + metrics: defaultMetrics, + maxDuration: .seconds(10), + thresholds: [ + .mallocCountTotal: .init(absolute: [.p90: 12000]), + .syscalls: .init(absolute: [.p90: 1600]), + ] ) ) { benchmark in let path = try AbsolutePath(validating: #file).parentDirectory.parentDirectory.parentDirectory diff --git a/Benchmarks/README.md b/Benchmarks/README.md new file mode 100644 index 00000000000..33a3a42ca99 --- /dev/null +++ b/Benchmarks/README.md @@ -0,0 +1,30 @@ +# SwiftPM Benchmarks + +Benchmarks currently use [ordo-one/package-benchmark](https://github.com/ordo-one/package-benchmark) library for benchmarking. + +## How to Run + +To run the benchmarks in their default configuration, run this commend in the `Benchmarks` subdirectory of the SwiftPM repository clone (the directory in which this `README.md` file is contained): +``` +swift package benchmark +``` + +To collect all benchmark metrics, set `SWIFTPM_BENCHMARK_ALL_METRICS` to a truthy value: + +``` +SWIFTPM_BENCHMARK_ALL_METRICS=true swift package benchmark +``` + +## Benchmark Thresholds + +`Benchmarks/Thresholds` subdirectory contains recorded allocation and syscall counts for macOS on Apple Silicon when built with Swift 5.10. To record new thresholds, run the following command: + +``` +swift package --allow-writing-to-package-directory benchmark --format metricP90AbsoluteThresholds --path Thresholds/ +``` + +To verify that recorded thresholds do not exceeded given relative or absolute values (passed as `thresholds` arguments to each benchmark's configuration), run this command: + +``` +swift package benchmark baseline check --check-absolute-path Thresholds/ +``` diff --git a/Benchmarks/Thresholds/PackageGraphBenchmarks.PackageGraphLoading.p90.json b/Benchmarks/Thresholds/PackageGraphBenchmarks.PackageGraphLoading.p90.json new file mode 100644 index 00000000000..1006180e80e --- /dev/null +++ b/Benchmarks/Thresholds/PackageGraphBenchmarks.PackageGraphLoading.p90.json @@ -0,0 +1,4 @@ +{ + "mallocCountTotal" : 9535, + "syscalls" : 1496 +} \ No newline at end of file From ec1bfe85e0dc6cff8766d3f633bd14f9f53a97f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Boris=20B=C3=BCgling?= Date: Wed, 17 Apr 2024 10:22:55 -0700 Subject: [PATCH 089/159] Remove use of `SWIFT_FORCE_*` build settings (#7464) These haven't been relevant for a while. --- Sources/XCBuildSupport/PIF.swift | 2 -- Sources/XCBuildSupport/PIFBuilder.swift | 2 -- .../XCBuildSupportTests/PIFBuilderTests.swift | 24 ------------------- 3 files changed, 28 deletions(-) diff --git a/Sources/XCBuildSupport/PIF.swift b/Sources/XCBuildSupport/PIF.swift index 2029bddca49..37953654c70 100644 --- a/Sources/XCBuildSupport/PIF.swift +++ b/Sources/XCBuildSupport/PIF.swift @@ -941,8 +941,6 @@ public enum PIF { case INSTALL_PATH case SUPPORTS_MACCATALYST case SWIFT_SERIALIZE_DEBUGGING_OPTIONS - case SWIFT_FORCE_STATIC_LINK_STDLIB - case SWIFT_FORCE_DYNAMIC_LINK_STDLIB case SWIFT_INSTALL_OBJC_HEADER case SWIFT_OBJC_INTERFACE_HEADER_NAME case SWIFT_OBJC_INTERFACE_HEADER_DIR diff --git a/Sources/XCBuildSupport/PIFBuilder.swift b/Sources/XCBuildSupport/PIFBuilder.swift index 986f9058085..3d77fd3b95d 100644 --- a/Sources/XCBuildSupport/PIFBuilder.swift +++ b/Sources/XCBuildSupport/PIFBuilder.swift @@ -432,8 +432,6 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { settings[.EXECUTABLE_NAME] = product.name settings[.CLANG_ENABLE_MODULES] = "YES" settings[.DEFINES_MODULE] = "YES" - settings[.SWIFT_FORCE_STATIC_LINK_STDLIB] = "NO" - settings[.SWIFT_FORCE_DYNAMIC_LINK_STDLIB] = "YES" if product.type == .executable || product.type == .test { settings[.LIBRARY_SEARCH_PATHS] = ["$(inherited)", "\(parameters.toolchainLibDir.pathString)/swift/macosx"] diff --git a/Tests/XCBuildSupportTests/PIFBuilderTests.swift b/Tests/XCBuildSupportTests/PIFBuilderTests.swift index c03033ecb9f..fa17ea9ebb8 100644 --- a/Tests/XCBuildSupportTests/PIFBuilderTests.swift +++ b/Tests/XCBuildSupportTests/PIFBuilderTests.swift @@ -500,8 +500,6 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.SDKROOT], "macosx") XCTAssertEqual(settings[.SKIP_INSTALL], "NO") XCTAssertEqual(settings[.SUPPORTED_PLATFORMS], ["macosx", "linux"]) - XCTAssertEqual(settings[.SWIFT_FORCE_DYNAMIC_LINK_STDLIB], "YES") - XCTAssertEqual(settings[.SWIFT_FORCE_STATIC_LINK_STDLIB], "NO") XCTAssertEqual(settings[.SWIFT_VERSION], "5") XCTAssertEqual(settings[.TARGET_NAME], "foo") XCTAssertEqual(settings[.LIBRARY_SEARCH_PATHS], ["$(inherited)", "/toolchain/lib/swift/macosx"]) @@ -524,8 +522,6 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.SDKROOT], "macosx") XCTAssertEqual(settings[.SKIP_INSTALL], "NO") XCTAssertEqual(settings[.SUPPORTED_PLATFORMS], ["macosx", "linux"]) - XCTAssertEqual(settings[.SWIFT_FORCE_DYNAMIC_LINK_STDLIB], "YES") - XCTAssertEqual(settings[.SWIFT_FORCE_STATIC_LINK_STDLIB], "NO") XCTAssertEqual(settings[.SWIFT_VERSION], "5") XCTAssertEqual(settings[.TARGET_NAME], "foo") XCTAssertEqual(settings[.LIBRARY_SEARCH_PATHS], ["$(inherited)", "/toolchain/lib/swift/macosx"]) @@ -562,8 +558,6 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.SDKROOT], "macosx") XCTAssertEqual(settings[.SKIP_INSTALL], "NO") XCTAssertEqual(settings[.SUPPORTED_PLATFORMS], ["macosx", "linux"]) - XCTAssertEqual(settings[.SWIFT_FORCE_DYNAMIC_LINK_STDLIB], "YES") - XCTAssertEqual(settings[.SWIFT_FORCE_STATIC_LINK_STDLIB], "NO") XCTAssertEqual(settings[.TARGET_NAME], "cfoo") XCTAssertEqual(settings[.LIBRARY_SEARCH_PATHS], ["$(inherited)", "/toolchain/lib/swift/macosx"]) } @@ -586,8 +580,6 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.SDKROOT], "macosx") XCTAssertEqual(settings[.SKIP_INSTALL], "NO") XCTAssertEqual(settings[.SUPPORTED_PLATFORMS], ["macosx", "linux"]) - XCTAssertEqual(settings[.SWIFT_FORCE_DYNAMIC_LINK_STDLIB], "YES") - XCTAssertEqual(settings[.SWIFT_FORCE_STATIC_LINK_STDLIB], "NO") XCTAssertEqual(settings[.TARGET_NAME], "cfoo") XCTAssertEqual(settings[.LIBRARY_SEARCH_PATHS], ["$(inherited)", "/toolchain/lib/swift/macosx"]) } @@ -622,8 +614,6 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.PRODUCT_NAME], "bar") XCTAssertEqual(settings[.SDKROOT], "macosx") XCTAssertEqual(settings[.SUPPORTED_PLATFORMS], ["macosx", "linux"]) - XCTAssertEqual(settings[.SWIFT_FORCE_DYNAMIC_LINK_STDLIB], "YES") - XCTAssertEqual(settings[.SWIFT_FORCE_STATIC_LINK_STDLIB], "NO") XCTAssertEqual(settings[.SWIFT_VERSION], "4.2") XCTAssertEqual(settings[.TARGET_NAME], "bar") XCTAssertEqual(settings[.LIBRARY_SEARCH_PATHS], ["$(inherited)", "/toolchain/lib/swift/macosx"]) @@ -643,8 +633,6 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.PRODUCT_NAME], "bar") XCTAssertEqual(settings[.SDKROOT], "macosx") XCTAssertEqual(settings[.SUPPORTED_PLATFORMS], ["macosx", "linux"]) - XCTAssertEqual(settings[.SWIFT_FORCE_DYNAMIC_LINK_STDLIB], "YES") - XCTAssertEqual(settings[.SWIFT_FORCE_STATIC_LINK_STDLIB], "NO") XCTAssertEqual(settings[.SWIFT_VERSION], "4.2") XCTAssertEqual(settings[.TARGET_NAME], "bar") XCTAssertEqual(settings[.LIBRARY_SEARCH_PATHS], ["$(inherited)", "/toolchain/lib/swift/macosx"]) @@ -680,8 +668,6 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.PRODUCT_NAME], "cbar") XCTAssertEqual(settings[.SDKROOT], "macosx") XCTAssertEqual(settings[.SUPPORTED_PLATFORMS], ["macosx", "linux"]) - XCTAssertEqual(settings[.SWIFT_FORCE_DYNAMIC_LINK_STDLIB], "YES") - XCTAssertEqual(settings[.SWIFT_FORCE_STATIC_LINK_STDLIB], "NO") XCTAssertEqual(settings[.TARGET_NAME], "cbar") XCTAssertEqual(settings[.LIBRARY_SEARCH_PATHS], ["$(inherited)", "/toolchain/lib/swift/macosx"]) } @@ -703,8 +689,6 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.PRODUCT_NAME], "cbar") XCTAssertEqual(settings[.SDKROOT], "macosx") XCTAssertEqual(settings[.SUPPORTED_PLATFORMS], ["macosx", "linux"]) - XCTAssertEqual(settings[.SWIFT_FORCE_DYNAMIC_LINK_STDLIB], "YES") - XCTAssertEqual(settings[.SWIFT_FORCE_STATIC_LINK_STDLIB], "NO") XCTAssertEqual(settings[.TARGET_NAME], "cbar") XCTAssertEqual(settings[.LIBRARY_SEARCH_PATHS], ["$(inherited)", "/toolchain/lib/swift/macosx"]) } @@ -835,8 +819,6 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.PRODUCT_BUNDLE_IDENTIFIER], "FooTests") XCTAssertEqual(settings[.PRODUCT_MODULE_NAME], "FooTests") XCTAssertEqual(settings[.PRODUCT_NAME], "FooTests") - XCTAssertEqual(settings[.SWIFT_FORCE_DYNAMIC_LINK_STDLIB], "YES") - XCTAssertEqual(settings[.SWIFT_FORCE_STATIC_LINK_STDLIB], "NO") XCTAssertEqual(settings[.SWIFT_VERSION], "5") XCTAssertEqual(settings[.TARGET_NAME], "FooTests") XCTAssertEqual(settings[.WATCHOS_DEPLOYMENT_TARGET], MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .watchOS).versionString) @@ -868,8 +850,6 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.PRODUCT_BUNDLE_IDENTIFIER], "FooTests") XCTAssertEqual(settings[.PRODUCT_MODULE_NAME], "FooTests") XCTAssertEqual(settings[.PRODUCT_NAME], "FooTests") - XCTAssertEqual(settings[.SWIFT_FORCE_DYNAMIC_LINK_STDLIB], "YES") - XCTAssertEqual(settings[.SWIFT_FORCE_STATIC_LINK_STDLIB], "NO") XCTAssertEqual(settings[.SWIFT_VERSION], "5") XCTAssertEqual(settings[.TARGET_NAME], "FooTests") XCTAssertEqual(settings[.WATCHOS_DEPLOYMENT_TARGET], MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .watchOS).versionString) @@ -916,8 +896,6 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.PRODUCT_BUNDLE_IDENTIFIER], "CFooTests") XCTAssertEqual(settings[.PRODUCT_MODULE_NAME], "CFooTests") XCTAssertEqual(settings[.PRODUCT_NAME], "CFooTests") - XCTAssertEqual(settings[.SWIFT_FORCE_DYNAMIC_LINK_STDLIB], "YES") - XCTAssertEqual(settings[.SWIFT_FORCE_STATIC_LINK_STDLIB], "NO") XCTAssertEqual(settings[.TARGET_NAME], "CFooTests") XCTAssertEqual(settings[.WATCHOS_DEPLOYMENT_TARGET], MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .watchOS).versionString) XCTAssertEqual(settings[.IPHONEOS_DEPLOYMENT_TARGET], MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .iOS).versionString) @@ -952,8 +930,6 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.PRODUCT_BUNDLE_IDENTIFIER], "CFooTests") XCTAssertEqual(settings[.PRODUCT_MODULE_NAME], "CFooTests") XCTAssertEqual(settings[.PRODUCT_NAME], "CFooTests") - XCTAssertEqual(settings[.SWIFT_FORCE_DYNAMIC_LINK_STDLIB], "YES") - XCTAssertEqual(settings[.SWIFT_FORCE_STATIC_LINK_STDLIB], "NO") XCTAssertEqual(settings[.TARGET_NAME], "CFooTests") XCTAssertEqual(settings[.WATCHOS_DEPLOYMENT_TARGET], MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .watchOS).versionString) XCTAssertEqual(settings[.IPHONEOS_DEPLOYMENT_TARGET], MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .iOS).versionString) From cb3b0856f3616e9a505c94d2c7958b83ab32f9f6 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Wed, 17 Apr 2024 21:27:51 +0100 Subject: [PATCH 090/159] Revert "Revert "Support macros when cross-compiling (#7118)" (#7352)" (#7353) Reverts apple/swift-package-manager#7352. Modified to build tests for the host triple when any macros are added to test modules directly as dependencies. --- CHANGELOG.md | 19 ++- .../Basics/Collections/IdentifiableSet.swift | 11 +- .../ClangTargetBuildDescription.swift | 6 +- .../ProductBuildDescription.swift | 4 +- .../ResolvedModule+BuildDescription.swift | 23 +++ .../SwiftTargetBuildDescription.swift | 141 +++++++++------- .../TargetBuildDescription.swift | 2 +- .../LLBuildManifestBuilder+Clang.swift | 2 +- .../LLBuildManifestBuilder+Product.swift | 6 +- .../LLBuildManifestBuilder+Swift.swift | 36 ++-- .../LLBuildManifestBuilder.swift | 26 +-- Sources/Build/BuildOperation.swift | 37 +++-- .../Build/BuildPlan/BuildPlan+Product.swift | 12 +- Sources/Build/BuildPlan/BuildPlan+Swift.swift | 4 +- Sources/Build/BuildPlan/BuildPlan+Test.swift | 61 ++++--- Sources/Build/BuildPlan/BuildPlan.swift | 79 +++++---- Sources/Build/CMakeLists.txt | 1 + .../PackageCommands/PluginCommand.swift | 11 +- Sources/Commands/SwiftTestCommand.swift | 6 +- .../Commands/Utilities/PluginDelegate.swift | 2 +- .../Utilities/SymbolGraphExtract.swift | 2 +- .../Commands/Utilities/TestingSupport.swift | 15 +- Sources/Commands/Utilities/XCTEvents.swift | 18 +- Sources/PackageGraph/BuildTriple.swift | 23 +++ Sources/PackageGraph/CMakeLists.txt | 2 +- .../PackageGraph/ModulesGraph+Loading.swift | 30 ++-- Sources/PackageGraph/ModulesGraph.swift | 118 +++++++++---- ...olvedTarget.swift => ResolvedModule.swift} | 59 ++++++- .../Resolution/ResolvedPackage.swift | 2 +- .../Resolution/ResolvedProduct.swift | 58 ++++++- Sources/PackageModel/Target/Target.swift | 2 +- .../BuildParameters/BuildParameters.swift | 10 +- .../BuildSystem/BuildSystem.swift | 2 +- .../Plugins/PluginContextSerializer.swift | 2 +- .../Plugins/PluginInvocation.swift | 12 +- Sources/SPMTestSupport/MockArchiver.swift | 2 +- .../SPMTestSupport/MockBuildTestHelper.swift | 42 +++-- .../SPMTestSupport/MockPackageGraphs.swift | 135 ++++++++++++++- .../SPMTestSupport/PackageGraphTester.swift | 120 +++++++++++--- .../SPMTestSupport/ResolvedTarget+Mock.swift | 8 +- Tests/BuildTests/BuildOperationTests.swift | 8 +- Tests/BuildTests/BuildPlanTests.swift | 115 +++++++------ .../ClangTargetBuildDescriptionTests.swift | 10 +- .../CrossCompilationBuildPlanTests.swift | 145 +++++++++++++++- .../LLBuildManifestBuilderTests.swift | 58 +++++-- .../BuildTests/ModuleAliasingBuildTests.swift | 26 +-- Tests/BuildTests/PluginsBuildPlanTests.swift | 24 ++- .../ProductBuildDescriptionTests.swift | 2 +- Tests/CommandsTests/PackageCommandTests.swift | 47 ++++-- .../SwiftCommandStateTests.swift | 8 +- .../PackageGraphPerfTests.swift | 4 +- .../CrossCompilationPackageGraphTests.swift | 156 ++++++++++++++++++ .../ResolvedTargetTests.swift | 68 ++++---- .../PluginInvocationTests.swift | 25 +-- .../SourceKitLSPAPITests.swift | 35 +++- 55 files changed, 1410 insertions(+), 472 deletions(-) create mode 100644 Sources/Build/BuildDescription/ResolvedModule+BuildDescription.swift rename Sources/PackageGraph/Resolution/{ResolvedTarget.swift => ResolvedModule.swift} (78%) create mode 100644 Tests/PackageGraphTests/CrossCompilationPackageGraphTests.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index fe5ed046fe5..61f95911802 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ Note: This is in reverse chronological order, so newer entries are added to the top. -Swift Next +Swift 6.0 ----------- * [#7202] @@ -8,11 +8,22 @@ Swift Next Package manifests can now access information about the Git repository the given package is in via the context object's `gitInformation` property. This allows to determine the current tag (if any), the current commit and whether or not there are uncommited changes. +* [#7201] + + `// swift-tools-version:` can now be specified on subsequent lines of `Package.swift`, for example when first few lines are required to contain a licensing comment header. + +* [#7118] + + Macros cross-compiled by SwiftPM with Swift SDKs are now correctly built, loaded, and evaluated for the host triple. + +Swift 5.10 +----------- + * [#7010] On macOS, `swift build` and `swift run` now produce binaries that allow backtraces in debug builds. Pass `SWIFT_BACKTRACE=enable=yes` environment variable to enable backtraces on such binaries when running them. -* [7101] +* [#7101] Binary artifacts are now cached along side repository checkouts so they do not need to be re-downloaded across projects. @@ -387,4 +398,8 @@ Swift 3.0 [#6276]: https://github.com/apple/swift-package-manager/pull/6276 [#6540]: https://github.com/apple/swift-package-manager/pull/6540 [#6663]: https://github.com/apple/swift-package-manager/pull/6663 +[#7010]: https://github.com/apple/swift-package-manager/pull/7010 [#7101]: https://github.com/apple/swift-package-manager/pull/7101 +[#7118]: https://github.com/apple/swift-package-manager/pull/7118 +[#7201]: https://github.com/apple/swift-package-manager/pull/7201 +[#7202]: https://github.com/apple/swift-package-manager/pull/7202 diff --git a/Sources/Basics/Collections/IdentifiableSet.swift b/Sources/Basics/Collections/IdentifiableSet.swift index b3bfec3071f..59bda6bdd85 100644 --- a/Sources/Basics/Collections/IdentifiableSet.swift +++ b/Sources/Basics/Collections/IdentifiableSet.swift @@ -45,13 +45,22 @@ public struct IdentifiableSet: Collection { } public subscript(id: Element.ID) -> Element? { - self.storage[id] + get { + self.storage[id] + } + set { + self.storage[id] = newValue + } } public func index(after i: Index) -> Index { Index(storageIndex: self.storage.index(after: i.storageIndex)) } + public mutating func insert(_ element: Element) { + self.storage[element.id] = element + } + public func union(_ otherSequence: some Sequence) -> Self { var result = self for element in otherSequence { diff --git a/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift b/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift index 4c62450d3c3..a1e22c52d6e 100644 --- a/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift @@ -53,7 +53,7 @@ package final class ClangTargetBuildDescription { /// Path to the bundle generated for this module (if any). var bundlePath: AbsolutePath? { - guard !resources.isEmpty else { + guard !self.resources.isEmpty else { return .none } @@ -133,7 +133,7 @@ package final class ClangTargetBuildDescription { self.target = target self.toolsVersion = toolsVersion self.buildParameters = buildParameters - self.tempsPath = buildParameters.buildPath.appending(component: target.c99name + ".build") + self.tempsPath = target.tempsPath(buildParameters) self.derivedSources = Sources(paths: [], root: tempsPath.appending("DerivedSources")) // We did not use to apply package plugins to C-family targets in prior tools-versions, this preserves the behavior. @@ -225,7 +225,7 @@ package final class ClangTargetBuildDescription { if self.buildParameters.triple.isDarwin() { args += ["-fobjc-arc"] } - args += try buildParameters.targetTripleArgs(for: target) + args += try self.buildParameters.tripleArgs(for: target) args += optimizationArguments args += activeCompilationConditions diff --git a/Sources/Build/BuildDescription/ProductBuildDescription.swift b/Sources/Build/BuildDescription/ProductBuildDescription.swift index 2c5538cbb3e..2277a6f638f 100644 --- a/Sources/Build/BuildDescription/ProductBuildDescription.swift +++ b/Sources/Build/BuildDescription/ProductBuildDescription.swift @@ -321,7 +321,7 @@ package final class ProductBuildDescription: SPMBuildCore.ProductBuildDescriptio // setting is the package-level right now. We might need to figure out a better // answer for libraries if/when we support specifying deployment target at the // target-level. - args += try self.buildParameters.targetTripleArgs(for: self.product.targets[self.product.targets.startIndex]) + args += try self.buildParameters.tripleArgs(for: self.product.targets[self.product.targets.startIndex]) // Add arguments from declared build settings. args += self.buildSettingsFlags @@ -356,7 +356,7 @@ package final class ProductBuildDescription: SPMBuildCore.ProductBuildDescriptio // Library search path for the toolchain's copy of SwiftSyntax. #if BUILD_MACROS_AS_DYLIBS if product.type == .macro { - args += try ["-L", buildParameters.toolchain.hostLibDir.pathString] + args += try ["-L", defaultBuildParameters.toolchain.hostLibDir.pathString] } #endif diff --git a/Sources/Build/BuildDescription/ResolvedModule+BuildDescription.swift b/Sources/Build/BuildDescription/ResolvedModule+BuildDescription.swift new file mode 100644 index 00000000000..59e5e2abacd --- /dev/null +++ b/Sources/Build/BuildDescription/ResolvedModule+BuildDescription.swift @@ -0,0 +1,23 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2015-2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import struct Basics.AbsolutePath +import struct PackageGraph.ResolvedModule + +import SPMBuildCore + +extension ResolvedModule { + func tempsPath(_ buildParameters: BuildParameters) -> AbsolutePath { + let suffix = buildParameters.suffix(triple: self.buildTriple) + return buildParameters.buildPath.appending(component: "\(self.c99name)\(suffix).build") + } +} diff --git a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift index 1b5777bcb80..286e6307dbc 100644 --- a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift @@ -42,8 +42,11 @@ package final class SwiftTargetBuildDescription { /// a target is built. package let toolsVersion: ToolsVersion - /// The build parameters. - let buildParameters: BuildParameters + /// The build parameters for this target. + let defaultBuildParameters: BuildParameters + + /// The build parameters for build tools. + let toolsBuildParameters: BuildParameters /// Path to the temporary directory for this target. let tempsPath: AbsolutePath @@ -62,7 +65,7 @@ package final class SwiftTargetBuildDescription { /// Path to the bundle generated for this module (if any). var bundlePath: AbsolutePath? { if let bundleName = target.underlying.potentialBundleName, needsResourceBundle { - return self.buildParameters.bundlePath(named: bundleName) + return self.defaultBuildParameters.bundlePath(named: bundleName) } else { return .none } @@ -97,7 +100,7 @@ package final class SwiftTargetBuildDescription { let relativeSources = self.target.sources.relativePaths + self.derivedSources.relativePaths + self.pluginDerivedSources.relativePaths - let ltoEnabled = self.buildParameters.linkingParameters.linkTimeOptimizationMode != nil + let ltoEnabled = self.defaultBuildParameters.linkingParameters.linkTimeOptimizationMode != nil let objectFileExtension = ltoEnabled ? "bc" : "o" return try relativeSources.map { try AbsolutePath( @@ -108,16 +111,17 @@ package final class SwiftTargetBuildDescription { } var modulesPath: AbsolutePath { - return self.buildParameters.buildPath.appending(component: "Modules") + let suffix = self.defaultBuildParameters.suffix(triple: self.target.buildTriple) + return self.defaultBuildParameters.buildPath.appending(component: "Modules\(suffix)") } /// The path to the swiftmodule file after compilation. public var moduleOutputPath: AbsolutePath { // note: needs to be `public` because of sourcekit-lsp // If we're an executable and we're not allowing test targets to link against us, we hide the module. - let triple = buildParameters.triple + let triple = defaultBuildParameters.triple let allowLinkingAgainstExecutables = (triple.isDarwin() || triple.isLinux() || triple.isWindows()) && self.toolsVersion >= .v5_5 let dirPath = (target.type == .executable && !allowLinkingAgainstExecutables) ? self.tempsPath : self.modulesPath - return dirPath.appending(component: self.target.c99name + ".swiftmodule") + return dirPath.appending(component: "\(self.target.c99name).swiftmodule") } /// The path to the wrapped swift module which is created using the modulewrap tool. This is required @@ -127,7 +131,7 @@ package final class SwiftTargetBuildDescription { self.tempsPath.appending(component: self.target.c99name + ".swiftmodule.o") } - /// The path to the swifinterface file after compilation. + /// The path to the swiftinterface file after compilation. var parseableModuleInterfaceOutputPath: AbsolutePath { self.modulesPath.appending(component: self.target.c99name + ".swiftinterface") } @@ -243,7 +247,7 @@ package final class SwiftTargetBuildDescription { private let shouldGenerateTestObservation: Bool /// Whether to disable sandboxing (e.g. for macros). - private let disableSandbox: Bool + private let shouldDisableSandbox: Bool /// Create a new target description with target and build parameters. init( @@ -251,13 +255,14 @@ package final class SwiftTargetBuildDescription { target: ResolvedModule, toolsVersion: ToolsVersion, additionalFileRules: [FileRuleDescription] = [], - buildParameters: BuildParameters, + destinationBuildParameters: BuildParameters, + toolsBuildParameters: BuildParameters, buildToolPluginInvocationResults: [BuildToolPluginInvocationResult] = [], prebuildCommandResults: [PrebuildCommandResult] = [], requiredMacroProducts: [ResolvedProduct] = [], testTargetRole: TestTargetRole? = nil, shouldGenerateTestObservation: Bool = false, - disableSandbox: Bool, + shouldDisableSandbox: Bool, fileSystem: FileSystem, observabilityScope: ObservabilityScope ) throws { @@ -269,7 +274,9 @@ package final class SwiftTargetBuildDescription { self.package = package self.target = target self.toolsVersion = toolsVersion - self.buildParameters = buildParameters + self.defaultBuildParameters = destinationBuildParameters + self.toolsBuildParameters = toolsBuildParameters + // Unless mentioned explicitly, use the target type to determine if this is a test target. if let testTargetRole { self.testTargetRole = testTargetRole @@ -279,13 +286,13 @@ package final class SwiftTargetBuildDescription { self.testTargetRole = nil } - self.tempsPath = buildParameters.buildPath.appending(component: target.c99name + ".build") + self.tempsPath = target.tempsPath(destinationBuildParameters) self.derivedSources = Sources(paths: [], root: self.tempsPath.appending("DerivedSources")) self.buildToolPluginInvocationResults = buildToolPluginInvocationResults self.prebuildCommandResults = prebuildCommandResults self.requiredMacroProducts = requiredMacroProducts self.shouldGenerateTestObservation = shouldGenerateTestObservation - self.disableSandbox = disableSandbox + self.shouldDisableSandbox = shouldDisableSandbox self.fileSystem = fileSystem self.observabilityScope = observabilityScope @@ -293,7 +300,7 @@ package final class SwiftTargetBuildDescription { target: target, toolsVersion: toolsVersion, additionalFileRules: additionalFileRules, - buildParameters: buildParameters, + buildParameters: destinationBuildParameters, buildToolPluginInvocationResults: buildToolPluginInvocationResults, prebuildCommandResults: prebuildCommandResults, observabilityScope: observabilityScope @@ -330,18 +337,22 @@ package final class SwiftTargetBuildDescription { return } - guard self.buildParameters.triple.isDarwin(), self.buildParameters.testingParameters.experimentalTestOutput else { + guard + self.defaultBuildParameters.triple.isDarwin() && + self.defaultBuildParameters.testingParameters.experimentalTestOutput + else { return } - let content = generateTestObservationCode(buildParameters: self.buildParameters) + let content = generateTestObservationCode(buildParameters: self.defaultBuildParameters) // FIXME: We should generate this file during the actual build. self.derivedSources.relativePaths.append(subpath) try self.fileSystem.writeIfChanged(path: path, string: content) } - // FIXME: This will not work well for large files, as we will store the entire contents, plus its byte array representation in memory and also `writeIfChanged()` will read the entire generated file again. + // FIXME: This will not work well for large files, as we will store the entire contents, plus its byte array + // representation in memory and also `writeIfChanged()` will read the entire generated file again. private func generateResourceEmbeddingCode() throws { guard needsResourceEmbedding else { return } @@ -374,7 +385,7 @@ package final class SwiftTargetBuildDescription { guard let bundlePath else { return } let mainPathSubstitution: String - if self.buildParameters.triple.isWASI() { + if self.defaultBuildParameters.triple.isWASI() { // We prefer compile-time evaluation of the bundle path here for WASI. There's no benefit in evaluating this // at runtime, especially as `Bundle` support in WASI Foundation is partial. We expect all resource paths to // evaluate to `/\(resourceBundleName)/\(resourcePath)`, which allows us to pass this path to JS APIs like @@ -426,12 +437,12 @@ package final class SwiftTargetBuildDescription { #if BUILD_MACROS_AS_DYLIBS self.requiredMacroProducts.forEach { macro in - args += ["-Xfrontend", "-load-plugin-library", "-Xfrontend", self.buildParameters.binaryPath(for: macro).pathString] + args += ["-Xfrontend", "-load-plugin-library", "-Xfrontend", self.toolsBuildParameters.binaryPath(for: macro).pathString] } #else try self.requiredMacroProducts.forEach { macro in if let macroTarget = macro.targets.first { - let executablePath = try self.buildParameters.binaryPath(for: macro).pathString + let executablePath = try self.toolsBuildParameters.binaryPath(for: macro).pathString args += ["-Xfrontend", "-load-plugin-executable", "-Xfrontend", "\(executablePath)#\(macroTarget.c99name)"] } else { throw InternalError("macro product \(macro.name) has no targets") // earlier validation should normally catch this @@ -440,13 +451,13 @@ package final class SwiftTargetBuildDescription { #endif // If we're using an OSS toolchain, add the required arguments bringing in the plugin server from the default toolchain if available. - if self.buildParameters.toolchain.isSwiftDevelopmentToolchain, + if self.defaultBuildParameters.toolchain.isSwiftDevelopmentToolchain, DriverSupport.checkSupportedFrontendFlags( flags: ["-external-plugin-path"], - toolchain: self.buildParameters.toolchain, + toolchain: self.defaultBuildParameters.toolchain, fileSystem: self.fileSystem ), - let pluginServer = try self.buildParameters.toolchain.swiftPluginServerPath + let pluginServer = try self.defaultBuildParameters.toolchain.swiftPluginServerPath { let toolchainUsrPath = pluginServer.parentDirectory.parentDirectory let pluginPathComponents = ["lib", "swift", "host", "plugins"] @@ -458,8 +469,12 @@ package final class SwiftTargetBuildDescription { args += ["-Xfrontend", "-external-plugin-path", "-Xfrontend", "\(localPluginPath)#\(pluginServer.pathString)"] } - if self.disableSandbox { - let toolchainSupportsDisablingSandbox = DriverSupport.checkSupportedFrontendFlags(flags: ["-disable-sandbox"], toolchain: self.buildParameters.toolchain, fileSystem: fileSystem) + if self.shouldDisableSandbox { + let toolchainSupportsDisablingSandbox = DriverSupport.checkSupportedFrontendFlags( + flags: ["-disable-sandbox"], + toolchain: self.defaultBuildParameters.toolchain, + fileSystem: fileSystem + ) if toolchainSupportsDisablingSandbox { args += ["-disable-sandbox"] } else { @@ -476,10 +491,10 @@ package final class SwiftTargetBuildDescription { /// The arguments needed to compile this target. package func compileArguments() throws -> [String] { var args = [String]() - args += try self.buildParameters.targetTripleArgs(for: self.target) + args += try self.defaultBuildParameters.tripleArgs(for: self.target) // pass `-v` during verbose builds. - if self.buildParameters.outputParameters.isVerbose { + if self.defaultBuildParameters.outputParameters.isVerbose { args += ["-v"] } @@ -487,22 +502,22 @@ package final class SwiftTargetBuildDescription { // // Technically, it should be enabled whenever WMO is off but we // don't currently make that distinction in SwiftPM - switch self.buildParameters.configuration { + switch self.defaultBuildParameters.configuration { case .debug: args += ["-enable-batch-mode"] case .release: break } - args += self.buildParameters.indexStoreArguments(for: self.target) + args += self.defaultBuildParameters.indexStoreArguments(for: self.target) args += self.optimizationArguments args += self.testingArguments - args += ["-j\(self.buildParameters.workers)"] + args += ["-j\(self.defaultBuildParameters.workers)"] args += self.activeCompilationConditions args += self.additionalFlags args += try self.moduleCacheArgs args += self.stdlibArguments - args += self.buildParameters.sanitizers.compileSwiftFlags() + args += self.defaultBuildParameters.sanitizers.compileSwiftFlags() args += ["-parseable-output"] // If we're compiling the main module of an executable other than the one that @@ -522,8 +537,8 @@ package final class SwiftTargetBuildDescription { // we can rename the symbol unconditionally. // No `-` for these flags because the set of Strings in driver.supportedFrontendFlags do // not have a leading `-` - if self.buildParameters.driverParameters.canRenameEntrypointFunctionName, - self.buildParameters.linkerFlagsForRenamingMainFunction(of: self.target) != nil + if self.defaultBuildParameters.driverParameters.canRenameEntrypointFunctionName, + self.defaultBuildParameters.linkerFlagsForRenamingMainFunction(of: self.target) != nil { args += ["-Xfrontend", "-entry-point-function-name", "-Xfrontend", "\(self.target.c99name)_main"] } @@ -536,7 +551,7 @@ package final class SwiftTargetBuildDescription { // Only add the build path to the framework search path if there are binary frameworks to link against. if !self.libraryBinaryPaths.isEmpty { - args += ["-F", self.buildParameters.buildPath.pathString] + args += ["-F", self.defaultBuildParameters.buildPath.pathString] } // Emit the ObjC compatibility header if enabled. @@ -545,12 +560,12 @@ package final class SwiftTargetBuildDescription { } // Add arguments needed for code coverage if it is enabled. - if self.buildParameters.testingParameters.enableCodeCoverage { + if self.defaultBuildParameters.testingParameters.enableCodeCoverage { args += ["-profile-coverage-mapping", "-profile-generate"] } // Add arguments to colorize output if stdout is tty - if self.buildParameters.outputParameters.isColorized { + if self.defaultBuildParameters.outputParameters.isColorized { args += ["-color-diagnostics"] } @@ -560,7 +575,7 @@ package final class SwiftTargetBuildDescription { switch testTargetRole { case .discovery, .entryPoint: for dependency in try self.target.recursiveTargetDependencies() { - let dependencyScope = self.buildParameters.createScope(for: dependency) + let dependencyScope = self.defaultBuildParameters.createScope(for: dependency) let dependencySwiftFlags = dependencyScope.evaluate(.OTHER_SWIFT_FLAGS) if let interopModeFlag = dependencySwiftFlags.first(where: { $0.hasPrefix("-cxx-interoperability-mode=") }) { args += [interopModeFlag] @@ -585,17 +600,17 @@ package final class SwiftTargetBuildDescription { // Add the output for the `.swiftinterface`, if requested or if library evolution has been enabled some other // way. - if self.buildParameters.driverParameters.enableParseableModuleInterfaces || args.contains("-enable-library-evolution") { + if self.defaultBuildParameters.driverParameters.enableParseableModuleInterfaces || args.contains("-enable-library-evolution") { args += ["-emit-module-interface-path", self.parseableModuleInterfaceOutputPath.pathString] } - args += self.buildParameters.toolchain.extraFlags.swiftCompilerFlags + args += self.defaultBuildParameters.toolchain.extraFlags.swiftCompilerFlags // User arguments (from -Xswiftc) should follow generated arguments to allow user overrides - args += self.buildParameters.flags.swiftCompilerFlags + args += self.defaultBuildParameters.flags.swiftCompilerFlags - args += self.buildParameters.toolchain.extraFlags.cCompilerFlags.asSwiftcCCompilerFlags() + args += self.defaultBuildParameters.toolchain.extraFlags.cCompilerFlags.asSwiftcCCompilerFlags() // User arguments (from -Xcc) should follow generated arguments to allow user overrides - args += self.buildParameters.flags.cCompilerFlags.asSwiftcCCompilerFlags() + args += self.defaultBuildParameters.flags.cCompilerFlags.asSwiftcCCompilerFlags() // TODO: Pass -Xcxx flags to swiftc (#6491) // Uncomment when downstream support arrives. @@ -604,7 +619,7 @@ package final class SwiftTargetBuildDescription { // args += self.buildParameters.flags.cxxCompilerFlags.asSwiftcCXXCompilerFlags() // Enable the correct LTO mode if requested. - switch self.buildParameters.linkingParameters.linkTimeOptimizationMode { + switch self.defaultBuildParameters.linkingParameters.linkTimeOptimizationMode { case nil: break case .full: @@ -614,7 +629,7 @@ package final class SwiftTargetBuildDescription { } // Pass default include paths from the toolchain. - for includeSearchPath in self.buildParameters.toolchain.includeSearchPaths { + for includeSearchPath in self.defaultBuildParameters.toolchain.includeSearchPaths { args += ["-I", includeSearchPath.pathString] } @@ -634,14 +649,14 @@ package final class SwiftTargetBuildDescription { args += self.package.packageNameArgument( target: self.target, - isPackageNameSupported: self.buildParameters.driverParameters.isPackageAccessModifierSupported + isPackageNameSupported: self.defaultBuildParameters.driverParameters.isPackageAccessModifierSupported ) args += try self.macroArguments() // rdar://117578677 // Pass -fno-omit-frame-pointer to support backtraces // this can be removed once the backtracer uses DWARF instead of frame pointers - if let omitFramePointers = self.buildParameters.debuggingParameters.omitFramePointers { + if let omitFramePointers = self.defaultBuildParameters.debuggingParameters.omitFramePointers { if omitFramePointers { args += ["-Xcc", "-fomit-frame-pointer"] } else { @@ -656,14 +671,14 @@ package final class SwiftTargetBuildDescription { /// such as emitting a module or supplementary outputs. package func emitCommandLine(scanInvocation: Bool = false) throws -> [String] { var result: [String] = [] - result.append(self.buildParameters.toolchain.swiftCompilerPath.pathString) + result.append(self.defaultBuildParameters.toolchain.swiftCompilerPath.pathString) result.append("-module-name") result.append(self.target.c99name) result.append( contentsOf: self.package.packageNameArgument( target: self.target, - isPackageNameSupported: self.buildParameters.driverParameters.isPackageAccessModifierSupported + isPackageNameSupported: self.defaultBuildParameters.driverParameters.isPackageAccessModifierSupported ) ) if !scanInvocation { @@ -679,7 +694,7 @@ package final class SwiftTargetBuildDescription { result.append(try self.writeOutputFileMap().pathString) } - if self.buildParameters.useWholeModuleOptimization { + if self.defaultBuildParameters.useWholeModuleOptimization { result.append("-whole-module-optimization") result.append("-num-threads") result.append(String(ProcessInfo.processInfo.activeProcessorCount)) @@ -699,7 +714,7 @@ package final class SwiftTargetBuildDescription { /// Returns true if ObjC compatibility header should be emitted. private var shouldEmitObjCCompatibilityHeader: Bool { - self.buildParameters.triple.isDarwin() && self.target.type == .library + self.defaultBuildParameters.triple.isDarwin() && self.target.type == .library } func writeOutputFileMap() throws -> AbsolutePath { @@ -713,7 +728,7 @@ package final class SwiftTargetBuildDescription { """# - if self.buildParameters.useWholeModuleOptimization { + if self.defaultBuildParameters.useWholeModuleOptimization { let moduleName = self.target.c99name content += #""" @@ -744,7 +759,7 @@ package final class SwiftTargetBuildDescription { // Write out the entries for each source file. let sources = self.sources let objects = try self.objects - let ltoEnabled = self.buildParameters.linkingParameters.linkTimeOptimizationMode != nil + let ltoEnabled = self.defaultBuildParameters.linkingParameters.linkTimeOptimizationMode != nil let objectKey = ltoEnabled ? "llvm-bc" : "object" for idx in 0.. [String] { - let scope = self.buildParameters.createScope(for: self.target) + let scope = self.defaultBuildParameters.createScope(for: self.target) var flags: [String] = [] // Swift defines. @@ -848,7 +863,7 @@ package final class SwiftTargetBuildDescription { // Include path for the toolchain's copy of SwiftSyntax. #if BUILD_MACROS_AS_DYLIBS if target.type == .macro { - flags += try ["-I", self.buildParameters.toolchain.hostLibDir.pathString] + flags += try ["-I", self.defaultBuildParameters.toolchain.hostLibDir.pathString] } #endif @@ -859,7 +874,7 @@ package final class SwiftTargetBuildDescription { private var activeCompilationConditions: [String] { var compilationConditions = ["-DSWIFT_PACKAGE"] - switch self.buildParameters.configuration { + switch self.defaultBuildParameters.configuration { case .debug: compilationConditions += ["-DDEBUG"] case .release: @@ -871,7 +886,7 @@ package final class SwiftTargetBuildDescription { /// Optimization arguments according to the build configuration. private var optimizationArguments: [String] { - switch self.buildParameters.configuration { + switch self.defaultBuildParameters.configuration { case .debug: return ["-Onone"] case .release: @@ -885,7 +900,7 @@ package final class SwiftTargetBuildDescription { // test targets must be built with -enable-testing // since its required for test discovery (the non objective-c reflection kind) return ["-enable-testing"] - } else if self.buildParameters.testingParameters.enableTestability { + } else if self.defaultBuildParameters.testingParameters.enableTestability { return ["-enable-testing"] } else { return [] @@ -895,20 +910,20 @@ package final class SwiftTargetBuildDescription { /// Module cache arguments. private var moduleCacheArgs: [String] { get throws { - ["-module-cache-path", try self.buildParameters.moduleCache.pathString] + ["-module-cache-path", try self.defaultBuildParameters.moduleCache.pathString] } } private var stdlibArguments: [String] { var arguments: [String] = [] - let isLinkingStaticStdlib = self.buildParameters.linkingParameters.shouldLinkStaticSwiftStdlib - && self.buildParameters.triple.isSupportingStaticStdlib + let isLinkingStaticStdlib = self.defaultBuildParameters.linkingParameters.shouldLinkStaticSwiftStdlib + && self.defaultBuildParameters.triple.isSupportingStaticStdlib if isLinkingStaticStdlib { arguments += ["-static-stdlib"] } - if let resourcesPath = self.buildParameters.toolchain.swiftResourcesPath(isStatic: isLinkingStaticStdlib) { + if let resourcesPath = self.defaultBuildParameters.toolchain.swiftResourcesPath(isStatic: isLinkingStaticStdlib) { arguments += ["-resource-dir", "\(resourcesPath)"] } diff --git a/Sources/Build/BuildDescription/TargetBuildDescription.swift b/Sources/Build/BuildDescription/TargetBuildDescription.swift index c5e14b9ac9e..b4d578947f2 100644 --- a/Sources/Build/BuildDescription/TargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/TargetBuildDescription.swift @@ -101,7 +101,7 @@ package enum TargetBuildDescription { var buildParameters: BuildParameters { switch self { case .swift(let swiftTargetBuildDescription): - return swiftTargetBuildDescription.buildParameters + return swiftTargetBuildDescription.defaultBuildParameters case .clang(let clangTargetBuildDescription): return clangTargetBuildDescription.buildParameters } diff --git a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Clang.swift b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Clang.swift index 70ae4ee508a..e6ddfc1a4fb 100644 --- a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Clang.swift +++ b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Clang.swift @@ -93,7 +93,7 @@ extension LLBuildManifestBuilder { let additionalInputs = try addBuildToolPlugins(.clang(target)) // Create a phony node to represent the entire target. - let targetName = target.target.getLLBuildTargetName(config: target.buildParameters.buildConfig) + let targetName = target.target.getLLBuildTargetName(buildParameters: target.buildParameters) let output: Node = .virtual(targetName) self.manifest.addNode(output, toTarget: targetName) diff --git a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Product.swift b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Product.swift index 3061b25ee7b..cf8a797ed9c 100644 --- a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Product.swift +++ b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Product.swift @@ -15,7 +15,7 @@ import struct LLBuildManifest.Node extension LLBuildManifestBuilder { func createProductCommand(_ buildProduct: ProductBuildDescription) throws { - let cmdName = try buildProduct.product.getCommandName(config: buildProduct.buildParameters.buildConfig) + let cmdName = try buildProduct.product.getCommandName(buildParameters: buildProduct.buildParameters) // Add dependency on Info.plist generation on Darwin platforms. let testInputs: [AbsolutePath] @@ -34,7 +34,7 @@ extension LLBuildManifestBuilder { } // Create a phony node to represent the entire target. - let targetName = try buildProduct.product.getLLBuildTargetName(config: buildProduct.buildParameters.buildConfig) + let targetName = try buildProduct.product.getLLBuildTargetName(buildParameters: buildProduct.buildParameters) let output: Node = .virtual(targetName) let finalProductNode: Node @@ -85,7 +85,7 @@ extension LLBuildManifestBuilder { outputPath: plistPath ) - let cmdName = try buildProduct.product.getCommandName(config: buildProduct.buildParameters.buildConfig) + let cmdName = try buildProduct.product.getCommandName(buildParameters: buildProduct.buildParameters) let codeSigningOutput = Node.virtual(targetName + "-CodeSigning") try self.manifest.addShellCmd( name: "\(cmdName)-entitlements", diff --git a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift index d499d231ebb..10fa290f50d 100644 --- a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift +++ b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift @@ -45,7 +45,7 @@ extension LLBuildManifestBuilder { let moduleNode = Node.file(target.moduleOutputPath) let cmdOutputs = objectNodes + [moduleNode] - if target.buildParameters.driverParameters.useIntegratedSwiftDriver { + if target.defaultBuildParameters.driverParameters.useIntegratedSwiftDriver { try self.addSwiftCmdsViaIntegratedDriver( target, inputs: inputs, @@ -68,7 +68,7 @@ extension LLBuildManifestBuilder { // jobs needed to build this Swift target. var commandLine = try target.emitCommandLine() commandLine.append("-driver-use-frontend-path") - commandLine.append(target.buildParameters.toolchain.swiftCompilerPath.pathString) + commandLine.append(target.defaultBuildParameters.toolchain.swiftCompilerPath.pathString) // FIXME: At some point SwiftPM should provide its own executor for // running jobs/launching processes during planning let resolver = try ArgsResolver(fileSystem: target.fileSystem) @@ -132,7 +132,7 @@ extension LLBuildManifestBuilder { // common intermediate dependency modules, such dependencies can lead // to cycles in the resulting manifest. var manifestNodeInputs: [Node] = [] - if targetDescription.buildParameters.driverParameters.useExplicitModuleBuild && !isMainModule(job) { + if targetDescription.defaultBuildParameters.driverParameters.useExplicitModuleBuild && !isMainModule(job) { manifestNodeInputs = jobInputs } else { manifestNodeInputs = (inputs + jobInputs).uniqued() @@ -192,7 +192,9 @@ extension LLBuildManifestBuilder { // Sort the product targets in topological order in order to collect and "bubble up" // their respective dependency graphs to the depending targets. let nodes: [ResolvedModule.Dependency] = try self.plan.targetMap.keys.compactMap { - guard let target = self.plan.graph.allTargets[$0] else { throw InternalError("unknown target \($0)") } + guard let target = self.plan.graph.allTargets[$0] else { + throw InternalError("unknown target \($0)") + } return ResolvedModule.Dependency.target(target, conditions: []) } let allPackageDependencies = try topologicalSort(nodes, successors: { $0.dependencies }) @@ -285,7 +287,7 @@ extension LLBuildManifestBuilder { // jobs needed to build this Swift target. var commandLine = try targetDescription.emitCommandLine() commandLine.append("-driver-use-frontend-path") - commandLine.append(targetDescription.buildParameters.toolchain.swiftCompilerPath.pathString) + commandLine.append(targetDescription.defaultBuildParameters.toolchain.swiftCompilerPath.pathString) commandLine.append("-experimental-explicit-module-build") let resolver = try ArgsResolver(fileSystem: self.fileSystem) let executor = SPMSwiftDriverExecutor( @@ -376,14 +378,14 @@ extension LLBuildManifestBuilder { cmdOutputs: [Node] ) throws { let isLibrary = target.target.type == .library || target.target.type == .test - let cmdName = target.target.getCommandName(config: target.buildParameters.buildConfig) + let cmdName = target.target.getCommandName(buildParameters: target.defaultBuildParameters) self.manifest.addWriteSourcesFileListCommand(sources: target.sources, sourcesFileListPath: target.sourcesFileListPath) self.manifest.addSwiftCmd( name: cmdName, inputs: inputs + [Node.file(target.sourcesFileListPath)], outputs: cmdOutputs, - executable: target.buildParameters.toolchain.swiftCompilerPath, + executable: target.defaultBuildParameters.toolchain.swiftCompilerPath, moduleName: target.target.c99name, moduleAliases: target.target.moduleAliases, moduleOutputPath: target.moduleOutputPath, @@ -394,7 +396,7 @@ extension LLBuildManifestBuilder { sources: target.sources, fileList: target.sourcesFileListPath, isLibrary: isLibrary, - wholeModuleOptimization: target.buildParameters.configuration == .release, + wholeModuleOptimization: target.defaultBuildParameters.configuration == .release, outputFileMapPath: try target.writeOutputFileMap() // FIXME: Eliminate side effect. ) } @@ -404,7 +406,7 @@ extension LLBuildManifestBuilder { ) throws -> [Node] { var inputs = target.sources.map(Node.file) - let swiftVersionFilePath = addSwiftGetVersionCommand(buildParameters: target.buildParameters) + let swiftVersionFilePath = addSwiftGetVersionCommand(buildParameters: target.defaultBuildParameters) inputs.append(.file(swiftVersionFilePath)) // Add resources node as the input to the target. This isn't great because we @@ -450,7 +452,7 @@ extension LLBuildManifestBuilder { } } - for dependency in target.target.dependencies(satisfying: target.buildParameters.buildEnvironment) { + for dependency in target.target.dependencies(satisfying: target.defaultBuildParameters.buildEnvironment) { switch dependency { case .target(let target, _): try addStaticTargetInputs(target) @@ -477,7 +479,7 @@ extension LLBuildManifestBuilder { } for binaryPath in target.libraryBinaryPaths { - let path = target.buildParameters.destinationPath(forBinaryAt: binaryPath) + let path = target.defaultBuildParameters.destinationPath(forBinaryAt: binaryPath) if self.fileSystem.isDirectory(binaryPath) { inputs.append(directory: path) } else { @@ -489,7 +491,7 @@ extension LLBuildManifestBuilder { // Depend on any required macro product's output. try target.requiredMacroProducts.forEach { macro in - try inputs.append(.virtual(macro.getLLBuildTargetName(config: target.buildParameters.buildConfig))) + try inputs.append(.virtual(macro.getLLBuildTargetName(buildParameters: target.defaultBuildParameters))) } return inputs + additionalInputs @@ -498,7 +500,7 @@ extension LLBuildManifestBuilder { /// Adds a top-level phony command that builds the entire target. private func addTargetCmd(_ target: SwiftTargetBuildDescription, cmdOutputs: [Node]) { // Create a phony node to represent the entire target. - let targetName = target.target.getLLBuildTargetName(config: target.buildParameters.buildConfig) + let targetName = target.target.getLLBuildTargetName(buildParameters: target.defaultBuildParameters) let targetOutput: Node = .virtual(targetName) self.manifest.addNode(targetOutput, toTarget: targetName) @@ -507,7 +509,7 @@ extension LLBuildManifestBuilder { inputs: cmdOutputs, outputs: [targetOutput] ) - if self.plan.graph.isInRootPackages(target.target, satisfying: target.buildParameters.buildEnvironment) { + if self.plan.graph.isInRootPackages(target.target, satisfying: target.defaultBuildParameters.buildEnvironment) { if !target.isTestTarget { self.addNode(targetOutput, toTarget: .main) } @@ -517,13 +519,13 @@ extension LLBuildManifestBuilder { private func addModuleWrapCmd(_ target: SwiftTargetBuildDescription) throws { // Add commands to perform the module wrapping Swift modules when debugging strategy is `modulewrap`. - guard target.buildParameters.debuggingStrategy == .modulewrap else { return } + guard target.defaultBuildParameters.debuggingStrategy == .modulewrap else { return } var moduleWrapArgs = [ - target.buildParameters.toolchain.swiftCompilerPath.pathString, + target.defaultBuildParameters.toolchain.swiftCompilerPath.pathString, "-modulewrap", target.moduleOutputPath.pathString, "-o", target.wrappedModuleOutputPath.pathString, ] - moduleWrapArgs += try target.buildParameters.targetTripleArgs(for: target.target) + moduleWrapArgs += try target.defaultBuildParameters.tripleArgs(for: target.target) self.manifest.addShellCmd( name: target.wrappedModuleOutputPath.pathString, description: "Wrapping AST for \(target.target.name) for debugging", diff --git a/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift b/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift index b1d781b7eb2..e9c1070b21c 100644 --- a/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift +++ b/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift @@ -317,31 +317,33 @@ extension TargetBuildDescription { } extension ResolvedModule { - package func getCommandName(config: String) -> String { - "C." + self.getLLBuildTargetName(config: config) + package func getCommandName(buildParameters: BuildParameters) -> String { + "C." + self.getLLBuildTargetName(buildParameters: buildParameters) } - package func getLLBuildTargetName(config: String) -> String { - "\(name)-\(config).module" + package func getLLBuildTargetName(buildParameters: BuildParameters) -> String { + "\(self.name)-\(buildParameters.buildConfig)\(buildParameters.suffix(triple: self.buildTriple)).module" } package func getLLBuildResourcesCmdName(config: String) -> String { - "\(name)-\(config).module-resources" + "\(self.name)-\(config).module-resources" } } extension ResolvedProduct { - package func getLLBuildTargetName(config: String) throws -> String { - let potentialExecutableTargetName = "\(name)-\(config).exe" - let potentialLibraryTargetName = "\(name)-\(config).dylib" + package func getLLBuildTargetName(buildParameters: BuildParameters) throws -> String { + let config = buildParameters.buildConfig + let suffix = buildParameters.suffix(triple: self.buildTriple) + let potentialExecutableTargetName = "\(name)-\(config)\(suffix).exe" + let potentialLibraryTargetName = "\(name)-\(config)\(suffix).dylib" switch type { case .library(.dynamic): return potentialLibraryTargetName case .test: - return "\(name)-\(config).test" + return "\(name)-\(config)\(suffix).test" case .library(.static): - return "\(name)-\(config).a" + return "\(name)-\(config)\(suffix).a" case .library(.automatic): throw InternalError("automatic library not supported") case .executable, .snippet: @@ -357,8 +359,8 @@ extension ResolvedProduct { } } - package func getCommandName(config: String) throws -> String { - try "C." + self.getLLBuildTargetName(config: config) + public func getCommandName(buildParameters: BuildParameters) throws -> String { + try "C.\(self.getLLBuildTargetName(buildParameters: buildParameters))\(buildParameters.suffix(triple: self.buildTriple))" } } diff --git a/Sources/Build/BuildOperation.swift b/Sources/Build/BuildOperation.swift index e4f4128d46c..9255948a749 100644 --- a/Sources/Build/BuildOperation.swift +++ b/Sources/Build/BuildOperation.swift @@ -11,14 +11,11 @@ //===----------------------------------------------------------------------===// import Basics - import LLBuildManifest import PackageGraph import PackageLoading import PackageModel - import SPMBuildCore - import SPMLLBuild import Foundation @@ -271,7 +268,7 @@ package final class BuildOperation: PackageStructureDelegate, SPMBuildCore.Build } // TODO: Currently this function will only match frameworks. - internal func detectUnexpressedDependencies( + func detectUnexpressedDependencies( availableLibraries: [LibraryMetadata], targetDependencyMap: [String: [String]]? ) { @@ -297,7 +294,9 @@ package final class BuildOperation: PackageStructureDelegate, SPMBuildCore.Build } let usedSDKDependencies: [String] = Set(possibleTempsPaths).flatMap { possibleTempsPath in - guard let contents = try? self.fileSystem.readFileContents(possibleTempsPath.appending(component: "\(c99name).d")) else { + guard let contents = try? self.fileSystem.readFileContents( + possibleTempsPath.appending(component: "\(c99name).d") + ) else { return [String]() } @@ -522,7 +521,7 @@ package final class BuildOperation: PackageStructureDelegate, SPMBuildCore.Build let graph = try getPackageGraph() if let result = subset.llbuildTargetName( for: graph, - config: self.productsBuildParameters.configuration.dirname, + buildParameters: self.productsBuildParameters, observabilityScope: self.observabilityScope ) { return result @@ -540,17 +539,20 @@ package final class BuildOperation: PackageStructureDelegate, SPMBuildCore.Build // Invoke any build tool plugins in the graph to generate prebuild commands and build commands. if let pluginConfiguration, !self.productsBuildParameters.shouldSkipBuilding { // Hacky workaround for rdar://120560817, but it replicates precisely enough the original behavior before - // products/tools build parameters were split. Ideally we want to have specify the correct path at the time + // products/tools build parameters were split. Ideally we want to specify the correct path at the time // when `toolsBuildParameters` is initialized, but we have too many places in the codebase where that's // done, which makes it hard to realign them all at once. var pluginsBuildParameters = self.toolsBuildParameters pluginsBuildParameters.dataPath = pluginsBuildParameters.dataPath.parentDirectory.appending(components: ["plugins", "tools"]) + var buildToolsGraph = graph + try buildToolsGraph.updateBuildTripleRecursively(.tools) + let buildOperationForPluginDependencies = BuildOperation( // FIXME: this doesn't maintain the products/tools split cleanly productsBuildParameters: pluginsBuildParameters, toolsBuildParameters: pluginsBuildParameters, cacheBuildManifest: false, - packageGraphLoader: { return graph }, + packageGraphLoader: { buildToolsGraph }, additionalFileRules: self.additionalFileRules, pkgConfigDirectories: self.pkgConfigDirectories, dependenciesByRootPackageIdentity: [:], @@ -560,7 +562,7 @@ package final class BuildOperation: PackageStructureDelegate, SPMBuildCore.Build fileSystem: self.fileSystem, observabilityScope: self.observabilityScope ) - buildToolPluginInvocationResults = try graph.invokeBuildToolPlugins( + buildToolPluginInvocationResults = try buildToolsGraph.invokeBuildToolPlugins( outputDir: pluginConfiguration.workDirectory.appending("outputs"), buildParameters: pluginsBuildParameters, additionalFileRules: self.additionalFileRules, @@ -578,7 +580,6 @@ package final class BuildOperation: PackageStructureDelegate, SPMBuildCore.Build } } - // Surface any diagnostics from build tool plugins. var succeeded = true for (_, (target, results)) in buildToolPluginInvocationResults { @@ -617,7 +618,7 @@ package final class BuildOperation: PackageStructureDelegate, SPMBuildCore.Build // Emit warnings about any unhandled files in authored packages. We do this after applying build tool plugins, once we know what files they handled. // rdar://113256834 This fix works for the plugins that do not have PreBuildCommands. - let targetsToConsider: [ResolvedTarget] + let targetsToConsider: [ResolvedModule] if let subset = subset, let recursiveDependencies = try subset.recursiveDependencies(for: graph, observabilityScope: observabilityScope) { targetsToConsider = recursiveDependencies @@ -658,7 +659,7 @@ package final class BuildOperation: PackageStructureDelegate, SPMBuildCore.Build // Create the build plan based, on the graph and any information from plugins. let plan = try BuildPlan( - productsBuildParameters: self.productsBuildParameters, + destinationBuildParameters: self.productsBuildParameters, toolsBuildParameters: self.toolsBuildParameters, graph: graph, additionalFileRules: additionalFileRules, @@ -901,9 +902,11 @@ extension BuildSubset { } /// Returns the name of the llbuild target that corresponds to the build subset. - func llbuildTargetName(for graph: ModulesGraph, config: String, observabilityScope: ObservabilityScope) - -> String? - { + func llbuildTargetName( + for graph: ModulesGraph, + buildParameters: BuildParameters, + observabilityScope: ObservabilityScope + ) -> String? { switch self { case .allExcludingTests: return LLBuildManifestBuilder.TargetKind.main.targetName @@ -924,14 +927,14 @@ extension BuildSubset { return LLBuildManifestBuilder.TargetKind.main.targetName } return observabilityScope.trap { - try product.getLLBuildTargetName(config: config) + try product.getLLBuildTargetName(buildParameters: buildParameters) } case .target(let targetName): guard let target = graph.allTargets.first(where: { $0.name == targetName }) else { observabilityScope.emit(error: "no target named '\(targetName)'") return nil } - return target.getLLBuildTargetName(config: config) + return target.getLLBuildTargetName(buildParameters: buildParameters) } } } diff --git a/Sources/Build/BuildPlan/BuildPlan+Product.swift b/Sources/Build/BuildPlan/BuildPlan+Product.swift index 6031c30775b..c3932f0d626 100644 --- a/Sources/Build/BuildPlan/BuildPlan+Product.swift +++ b/Sources/Build/BuildPlan/BuildPlan+Product.swift @@ -80,7 +80,7 @@ extension BuildPlan { switch target.underlying { case is SwiftTarget: // Swift targets are guaranteed to have a corresponding Swift description. - guard case .swift(let description) = targetMap[target.id] else { + guard case .swift(let description) = self.targetMap[target.id] else { throw InternalError("unknown target \(target)") } @@ -102,13 +102,13 @@ extension BuildPlan { buildProduct.staticTargets = dependencies.staticTargets buildProduct.dylibs = try dependencies.dylibs.map { - guard let product = productMap[$0.id] else { + guard let product = self.productMap[$0.id] else { throw InternalError("unknown product \($0)") } return product } buildProduct.objects += try dependencies.staticTargets.flatMap { targetName -> [AbsolutePath] in - guard let target = targetMap[targetName.id] else { + guard let target = self.targetMap[targetName.id] else { throw InternalError("unknown target \(targetName)") } return try target.objects @@ -230,9 +230,11 @@ extension BuildPlan { if product.targets.contains(id: target.id) { staticTargets.append(target) } - // Library targets should always be included. + // Library targets should always be included for the same build triple. case .library: - staticTargets.append(target) + if target.buildTriple == product.buildTriple { + staticTargets.append(target) + } // Add system target to system targets array. case .systemModule: systemModules.append(target) diff --git a/Sources/Build/BuildPlan/BuildPlan+Swift.swift b/Sources/Build/BuildPlan/BuildPlan+Swift.swift index 36b1cacde0c..058631598f2 100644 --- a/Sources/Build/BuildPlan/BuildPlan+Swift.swift +++ b/Sources/Build/BuildPlan/BuildPlan+Swift.swift @@ -19,7 +19,7 @@ extension BuildPlan { func plan(swiftTarget: SwiftTargetBuildDescription) throws { // We need to iterate recursive dependencies because Swift compiler needs to see all the targets a target // depends on. - let environment = swiftTarget.buildParameters.buildEnvironment + let environment = swiftTarget.defaultBuildParameters.buildEnvironment for case .target(let dependency, _) in try swiftTarget.target.recursiveDependencies(satisfying: environment) { switch dependency.underlying { case let underlyingTarget as ClangTarget where underlyingTarget.type == .library: @@ -40,7 +40,7 @@ extension BuildPlan { swiftTarget.additionalFlags += try pkgConfig(for: target).cFlags case let target as BinaryTarget: if case .xcframework = target.kind { - let libraries = try self.parseXCFramework(for: target, triple: swiftTarget.buildParameters.triple) + let libraries = try self.parseXCFramework(for: target, triple: swiftTarget.defaultBuildParameters.triple) for library in libraries { library.headersPaths.forEach { swiftTarget.additionalFlags += ["-I", $0.pathString, "-Xcc", "-I", "-Xcc", $0.pathString] diff --git a/Sources/Build/BuildPlan/BuildPlan+Test.swift b/Sources/Build/BuildPlan/BuildPlan+Test.swift index bf1838af200..23c03da9363 100644 --- a/Sources/Build/BuildPlan/BuildPlan+Test.swift +++ b/Sources/Build/BuildPlan/BuildPlan+Test.swift @@ -26,15 +26,16 @@ import protocol TSCBasic.FileSystem extension BuildPlan { static func makeDerivedTestTargets( - _ buildParameters: BuildParameters, + destinationBuildParameters: BuildParameters, + toolsBuildParameters: BuildParameters, _ graph: ModulesGraph, - _ disableSandbox: Bool, + shouldDisableSandbox: Bool, _ fileSystem: FileSystem, _ observabilityScope: ObservabilityScope ) throws -> [(product: ResolvedProduct, discoveryTargetBuildDescription: SwiftTargetBuildDescription?, entryPointTargetBuildDescription: SwiftTargetBuildDescription)] { - guard buildParameters.testingParameters.testProductStyle.requiresAdditionalDerivedTestTargets, - case .entryPointExecutable(let explicitlyEnabledDiscovery, let explicitlySpecifiedPath) = - buildParameters.testingParameters.testProductStyle + guard destinationBuildParameters.testingParameters.testProductStyle.requiresAdditionalDerivedTestTargets, + case .entryPointExecutable(let explicitlyEnabledDiscovery, let explicitlySpecifiedPath) = + destinationBuildParameters.testingParameters.testProductStyle else { throw InternalError("makeTestManifestTargets should not be used for build plan which does not require additional derived test targets") } @@ -68,7 +69,7 @@ extension BuildPlan { /// Generates test discovery targets, which contain derived sources listing the discovered tests. func generateDiscoveryTargets() throws -> (target: SwiftTarget, resolved: ResolvedModule, buildDescription: SwiftTargetBuildDescription) { let discoveryTargetName = "\(package.manifest.displayName)PackageDiscoveredTests" - let discoveryDerivedDir = buildParameters.buildPath.appending(components: "\(discoveryTargetName).derived") + let discoveryDerivedDir = destinationBuildParameters.buildPath.appending(components: "\(discoveryTargetName).derived") let discoveryMainFile = discoveryDerivedDir.appending(component: TestDiscoveryTool.mainFileName) var discoveryPaths: [AbsolutePath] = [] @@ -84,7 +85,7 @@ extension BuildPlan { packageAccess: true, // test target is allowed access to package decls by default testDiscoverySrc: Sources(paths: discoveryPaths, root: discoveryDerivedDir) ) - let discoveryResolvedTarget = ResolvedModule( + var discoveryResolvedTarget = ResolvedModule( packageIdentity: testProduct.packageIdentity, underlying: discoveryTarget, dependencies: testProduct.targets.map { .target($0, conditions: []) }, @@ -92,13 +93,23 @@ extension BuildPlan { supportedPlatforms: testProduct.supportedPlatforms, platformVersionProvider: testProduct.platformVersionProvider ) + + discoveryResolvedTarget.buildTriple = testProduct.buildTriple + let discoveryTargetBuildParameters: BuildParameters + switch discoveryResolvedTarget.buildTriple { + case .tools: + discoveryTargetBuildParameters = toolsBuildParameters + case .destination: + discoveryTargetBuildParameters = destinationBuildParameters + } let discoveryTargetBuildDescription = try SwiftTargetBuildDescription( package: package, target: discoveryResolvedTarget, toolsVersion: toolsVersion, - buildParameters: buildParameters, + destinationBuildParameters: discoveryTargetBuildParameters, + toolsBuildParameters: toolsBuildParameters, testTargetRole: .discovery, - disableSandbox: disableSandbox, + shouldDisableSandbox: shouldDisableSandbox, fileSystem: fileSystem, observabilityScope: observabilityScope ) @@ -112,8 +123,8 @@ extension BuildPlan { swiftTargetDependencies: [Target.Dependency], resolvedTargetDependencies: [ResolvedModule.Dependency] ) throws -> SwiftTargetBuildDescription { - let entryPointDerivedDir = buildParameters.buildPath.appending(components: "\(testProduct.name).derived") - let entryPointMainFileName = TestEntryPointTool.mainFileName(for: buildParameters.testingParameters.library) + let entryPointDerivedDir = destinationBuildParameters.buildPath.appending(components: "\(testProduct.name).derived") + let entryPointMainFileName = TestEntryPointTool.mainFileName(for: destinationBuildParameters.testingParameters.library) let entryPointMainFile = entryPointDerivedDir.appending(component: entryPointMainFileName) let entryPointSources = Sources(paths: [entryPointMainFile], root: entryPointDerivedDir) @@ -124,7 +135,7 @@ extension BuildPlan { packageAccess: true, // test target is allowed access to package decls testEntryPointSources: entryPointSources ) - let entryPointResolvedTarget = ResolvedModule( + var entryPointResolvedTarget = ResolvedModule( packageIdentity: testProduct.packageIdentity, underlying: entryPointTarget, dependencies: testProduct.targets.map { .target($0, conditions: []) } + resolvedTargetDependencies, @@ -132,13 +143,23 @@ extension BuildPlan { supportedPlatforms: testProduct.supportedPlatforms, platformVersionProvider: testProduct.platformVersionProvider ) + entryPointResolvedTarget.buildTriple = testProduct.buildTriple + let entryPointBuildParameters: BuildParameters + switch entryPointResolvedTarget.buildTriple { + case .tools: + entryPointBuildParameters = toolsBuildParameters + case .destination: + entryPointBuildParameters = destinationBuildParameters + } + return try SwiftTargetBuildDescription( package: package, target: entryPointResolvedTarget, toolsVersion: toolsVersion, - buildParameters: buildParameters, + destinationBuildParameters: entryPointBuildParameters, + toolsBuildParameters: toolsBuildParameters, testTargetRole: .entryPoint(isSynthesized: true), - disableSandbox: disableSandbox, + shouldDisableSandbox: shouldDisableSandbox, fileSystem: fileSystem, observabilityScope: observabilityScope ) @@ -148,7 +169,7 @@ extension BuildPlan { let swiftTargetDependencies: [Target.Dependency] let resolvedTargetDependencies: [ResolvedModule.Dependency] - switch buildParameters.testingParameters.library { + switch destinationBuildParameters.testingParameters.library { case .xctest: discoveryTargets = try generateDiscoveryTargets() swiftTargetDependencies = [.target(discoveryTargets!.target, conditions: [])] @@ -181,9 +202,10 @@ extension BuildPlan { package: package, target: entryPointResolvedTarget, toolsVersion: toolsVersion, - buildParameters: buildParameters, + destinationBuildParameters: destinationBuildParameters, + toolsBuildParameters: toolsBuildParameters, testTargetRole: .entryPoint(isSynthesized: false), - disableSandbox: disableSandbox, + shouldDisableSandbox: shouldDisableSandbox, fileSystem: fileSystem, observabilityScope: observabilityScope ) @@ -203,9 +225,10 @@ extension BuildPlan { package: package, target: entryPointResolvedTarget, toolsVersion: toolsVersion, - buildParameters: buildParameters, + destinationBuildParameters: destinationBuildParameters, + toolsBuildParameters: toolsBuildParameters, testTargetRole: .entryPoint(isSynthesized: false), - disableSandbox: disableSandbox, + shouldDisableSandbox: shouldDisableSandbox, fileSystem: fileSystem, observabilityScope: observabilityScope ) diff --git a/Sources/Build/BuildPlan/BuildPlan.swift b/Sources/Build/BuildPlan/BuildPlan.swift index 0c13a3daed9..0f4a9e06e8d 100644 --- a/Sources/Build/BuildPlan/BuildPlan.swift +++ b/Sources/Build/BuildPlan/BuildPlan.swift @@ -128,12 +128,13 @@ extension BuildParameters { } /// Computes the target triple arguments for a given resolved target. - package func targetTripleArgs(for target: ResolvedModule) throws -> [String] { + package func tripleArgs(for target: ResolvedModule) throws -> [String] { + // confusingly enough this is the triple argument, not the target argument var args = ["-target"] // Compute the triple string for Darwin platform using the platform version. if self.triple.isDarwin() { - let platform = buildEnvironment.platform + let platform = self.buildEnvironment.platform let supportedPlatform = target.getSupportedPlatform(for: platform, usingXCTest: target.type == .test) args += [self.triple.tripleString(forPlatformVersion: supportedPlatform.version.versionString)] } else { @@ -185,16 +186,6 @@ public class BuildPlan: SPMBuildCore.BuildPlan { /// Build parameters used for tools. public let toolsBuildParameters: BuildParameters - /// Triple for which this target is compiled. - private func buildTriple(for target: ResolvedModule) -> Basics.Triple { - self.buildParameters(for: target).triple - } - - /// Triple for which this product is compiled. - private func buildTriple(for product: ResolvedProduct) -> Basics.Triple { - self.buildParameters(for: product).triple - } - /// The package graph. package let graph: ModulesGraph @@ -230,14 +221,14 @@ public class BuildPlan: SPMBuildCore.BuildPlan { /// Cache for pkgConfig flags. private var pkgConfigCache = [SystemLibraryTarget: (cFlags: [String], libs: [String])]() - /// Cache for library information. + /// Cache for library information. private var externalLibrariesCache = [BinaryTarget: [LibraryInfo]]() - /// Cache for tools information. + /// Cache for tools information. var externalExecutablesCache = [BinaryTarget: [ExecutableInfo]]() /// Whether to disable sandboxing (e.g. for macros). - private let disableSandbox: Bool + private let shouldDisableSandbox: Bool /// The filesystem to operate on. let fileSystem: any FileSystem @@ -245,18 +236,18 @@ public class BuildPlan: SPMBuildCore.BuildPlan { /// ObservabilityScope with which to emit diagnostics let observabilityScope: ObservabilityScope - @available(*, deprecated, renamed: "init(productsBuildParameters:toolsBuildParameters:graph:)") + @available(*, deprecated, renamed: "init(destinationBuildParameters:toolsBuildParameters:graph:)") public convenience init( buildParameters: BuildParameters, graph: ModulesGraph, additionalFileRules: [FileRuleDescription] = [], - buildToolPluginInvocationResults: [ResolvedTarget.ID: [BuildToolPluginInvocationResult]] = [:], - prebuildCommandResults: [ResolvedTarget.ID: [PrebuildCommandResult]] = [:], + buildToolPluginInvocationResults: [ResolvedModule.ID: [BuildToolPluginInvocationResult]] = [:], + prebuildCommandResults: [ResolvedModule.ID: [PrebuildCommandResult]] = [:], fileSystem: any FileSystem, observabilityScope: ObservabilityScope ) throws { try self.init( - productsBuildParameters: buildParameters, + destinationBuildParameters: buildParameters, toolsBuildParameters: buildParameters, graph: graph, additionalFileRules: additionalFileRules, @@ -267,11 +258,29 @@ public class BuildPlan: SPMBuildCore.BuildPlan { ) } - /// Create a build plan with a package graph and explicitly distinct build parameters for products and tools. - public init( + @available(*, deprecated, renamed: "init(destinationBuildParameters:toolsBuildParameters:graph:fileSystem:observabilityScope:)") + public convenience init( productsBuildParameters: BuildParameters, toolsBuildParameters: BuildParameters, graph: ModulesGraph, + fileSystem: any FileSystem, + observabilityScope: ObservabilityScope + ) throws { + try self.init( + destinationBuildParameters: productsBuildParameters, + toolsBuildParameters: toolsBuildParameters, + graph: graph, + fileSystem: fileSystem, + observabilityScope: observabilityScope + ) + } + + /// Create a build plan with a package graph and explicitly distinct build parameters for destination platform and + /// tools platform. + public init( + destinationBuildParameters: BuildParameters, + toolsBuildParameters: BuildParameters, + graph: ModulesGraph, additionalFileRules: [FileRuleDescription] = [], buildToolPluginInvocationResults: [ResolvedModule.ID: [BuildToolPluginInvocationResult]] = [:], prebuildCommandResults: [ResolvedModule.ID: [PrebuildCommandResult]] = [:], @@ -279,16 +288,17 @@ public class BuildPlan: SPMBuildCore.BuildPlan { fileSystem: any FileSystem, observabilityScope: ObservabilityScope ) throws { - self.destinationBuildParameters = productsBuildParameters + self.destinationBuildParameters = destinationBuildParameters self.toolsBuildParameters = toolsBuildParameters self.graph = graph self.buildToolPluginInvocationResults = buildToolPluginInvocationResults self.prebuildCommandResults = prebuildCommandResults - self.disableSandbox = disableSandbox + self.shouldDisableSandbox = disableSandbox self.fileSystem = fileSystem self.observabilityScope = observabilityScope.makeChildScope(description: "Build Plan") - var productMap: [ResolvedProduct.ID: (product: ResolvedProduct, buildDescription: ProductBuildDescription)] = [:] + var productMap: [ResolvedProduct.ID: (product: ResolvedProduct, buildDescription: ProductBuildDescription)] = + [:] // Create product description for each product we have in the package graph that is eligible. for product in graph.allProducts where product.shouldCreateProductDescription { let buildParameters: BuildParameters @@ -296,7 +306,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan { case .tools: buildParameters = toolsBuildParameters case .destination: - buildParameters = productsBuildParameters + buildParameters = destinationBuildParameters } guard let package = graph.package(for: product) else { @@ -334,7 +344,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan { case .tools: buildParameters = toolsBuildParameters case .destination: - buildParameters = productsBuildParameters + buildParameters = destinationBuildParameters } // Validate the product dependencies of this target. @@ -383,12 +393,13 @@ public class BuildPlan: SPMBuildCore.BuildPlan { target: target, toolsVersion: toolsVersion, additionalFileRules: additionalFileRules, - buildParameters: buildParameters, + destinationBuildParameters: buildParameters, + toolsBuildParameters: toolsBuildParameters, buildToolPluginInvocationResults: buildToolPluginInvocationResults[target.id] ?? [], prebuildCommandResults: prebuildCommandResults[target.id] ?? [], requiredMacroProducts: requiredMacroProducts, shouldGenerateTestObservation: generateTestObservation, - disableSandbox: self.disableSandbox, + shouldDisableSandbox: self.shouldDisableSandbox, fileSystem: fileSystem, observabilityScope: observabilityScope ) @@ -440,19 +451,21 @@ public class BuildPlan: SPMBuildCore.BuildPlan { } // Plan the derived test targets, if necessary. - if productsBuildParameters.testingParameters.testProductStyle.requiresAdditionalDerivedTestTargets { + if destinationBuildParameters.testingParameters.testProductStyle.requiresAdditionalDerivedTestTargets { let derivedTestTargets = try Self.makeDerivedTestTargets( - productsBuildParameters, + destinationBuildParameters: destinationBuildParameters, + toolsBuildParameters: toolsBuildParameters, graph, - self.disableSandbox, + shouldDisableSandbox: self.shouldDisableSandbox, self.fileSystem, self.observabilityScope ) for item in derivedTestTargets { var derivedTestTargets = [item.entryPointTargetBuildDescription.target] - targetMap[item.entryPointTargetBuildDescription.target.id] = - .swift(item.entryPointTargetBuildDescription) + targetMap[item.entryPointTargetBuildDescription.target.id] = .swift( + item.entryPointTargetBuildDescription + ) if let discoveryTargetBuildDescription = item.discoveryTargetBuildDescription { targetMap[discoveryTargetBuildDescription.target.id] = .swift(discoveryTargetBuildDescription) diff --git a/Sources/Build/CMakeLists.txt b/Sources/Build/CMakeLists.txt index fcc51aed76d..9412aa38590 100644 --- a/Sources/Build/CMakeLists.txt +++ b/Sources/Build/CMakeLists.txt @@ -10,6 +10,7 @@ add_library(Build BuildDescription/ClangTargetBuildDescription.swift BuildDescription/PluginDescription.swift BuildDescription/ProductBuildDescription.swift + BuildDescription/ResolvedModule+BuildDescription.swift BuildDescription/SwiftTargetBuildDescription.swift BuildDescription/TargetBuildDescription.swift BuildManifest/LLBuildManifestBuilder.swift diff --git a/Sources/Commands/PackageCommands/PluginCommand.swift b/Sources/Commands/PackageCommands/PluginCommand.swift index b7620cdec77..3255e1d854b 100644 --- a/Sources/Commands/PackageCommands/PluginCommand.swift +++ b/Sources/Commands/PackageCommands/PluginCommand.swift @@ -18,7 +18,9 @@ import CoreCommands import SPMBuildCore import Dispatch + import PackageGraph + import PackageModel import enum TSCBasic.ProcessEnv @@ -319,6 +321,9 @@ struct PluginCommand: SwiftCommand { let toolSearchDirs = [try swiftCommandState.getTargetToolchain().swiftCompilerPath.parentDirectory] + getEnvSearchPaths(pathString: ProcessEnv.path, currentWorkingDirectory: .none) + var buildToolsGraph = packageGraph + try buildToolsGraph.updateBuildTripleRecursively(.tools) + let buildParameters = try swiftCommandState.toolsBuildParameters // Build or bring up-to-date any executable host-side tools on which this plugin depends. Add them and any binary dependencies to the tool-names-to-path map. let buildSystem = try swiftCommandState.createBuildSystem( @@ -327,10 +332,12 @@ struct PluginCommand: SwiftCommand { // Force all dependencies to be built for the host, to work around the fact that BuildOperation.plan // knows to compile build tool plugin dependencies for the host but does not do the same for command // plugins. - productsBuildParameters: buildParameters + productsBuildParameters: buildParameters, + packageGraphLoader: { buildToolsGraph } ) + let accessibleTools = try plugin.processAccessibleTools( - packageGraph: packageGraph, + packageGraph: buildToolsGraph, fileSystem: swiftCommandState.fileSystem, environment: buildParameters.buildEnvironment, for: try pluginScriptRunner.hostTriple diff --git a/Sources/Commands/SwiftTestCommand.swift b/Sources/Commands/SwiftTestCommand.swift index bd2ebf02208..1650c2eb663 100644 --- a/Sources/Commands/SwiftTestCommand.swift +++ b/Sources/Commands/SwiftTestCommand.swift @@ -401,7 +401,7 @@ package struct SwiftTestCommand: AsyncSwiftCommand { let toolchain = try swiftCommandState.getTargetToolchain() let testEnv = try TestingSupport.constructTestEnvironment( toolchain: toolchain, - buildParameters: buildParameters, + destinationBuildParameters: buildParameters, sanitizers: globalOptions.build.sanitizers ) @@ -676,7 +676,7 @@ extension SwiftTestCommand { let toolchain = try swiftCommandState.getTargetToolchain() let testEnv = try TestingSupport.constructTestEnvironment( toolchain: toolchain, - buildParameters: buildParameters, + destinationBuildParameters: buildParameters, sanitizers: globalOptions.build.sanitizers ) @@ -985,7 +985,7 @@ final class ParallelTestRunner { let testEnv = try TestingSupport.constructTestEnvironment( toolchain: self.toolchain, - buildParameters: self.buildParameters, + destinationBuildParameters: self.buildParameters, sanitizers: self.buildOptions.sanitizers ) diff --git a/Sources/Commands/Utilities/PluginDelegate.swift b/Sources/Commands/Utilities/PluginDelegate.swift index 30d50f87dae..82960b6e0c8 100644 --- a/Sources/Commands/Utilities/PluginDelegate.swift +++ b/Sources/Commands/Utilities/PluginDelegate.swift @@ -238,7 +238,7 @@ final class PluginDelegate: PluginInvocationDelegate { // Construct the environment we'll pass down to the tests. let testEnvironment = try TestingSupport.constructTestEnvironment( toolchain: toolchain, - buildParameters: toolsBuildParameters, + destinationBuildParameters: toolsBuildParameters, sanitizers: swiftCommandState.options.build.sanitizers ) diff --git a/Sources/Commands/Utilities/SymbolGraphExtract.swift b/Sources/Commands/Utilities/SymbolGraphExtract.swift index e30fb813f1a..03c3afde7bd 100644 --- a/Sources/Commands/Utilities/SymbolGraphExtract.swift +++ b/Sources/Commands/Utilities/SymbolGraphExtract.swift @@ -66,7 +66,7 @@ package struct SymbolGraphExtract { // Construct arguments for extracting symbols for a single target. var commandLine = [self.tool.pathString] commandLine += ["-module-name", module.c99name] - commandLine += try buildParameters.targetTripleArgs(for: module) + commandLine += try buildParameters.tripleArgs(for: module) commandLine += try buildPlan.createAPIToolCommonArgs(includeLibrarySearchPaths: true) commandLine += ["-module-cache-path", try buildParameters.moduleCache.pathString] if verboseOutput { diff --git a/Sources/Commands/Utilities/TestingSupport.swift b/Sources/Commands/Utilities/TestingSupport.swift index d7643d330e7..d582223fc08 100644 --- a/Sources/Commands/Utilities/TestingSupport.swift +++ b/Sources/Commands/Utilities/TestingSupport.swift @@ -38,7 +38,9 @@ enum TestingSupport { func findXCTestHelper(swiftBuildPath: AbsolutePath) -> AbsolutePath? { // XCTestHelper tool is installed in libexec. - let maybePath = swiftBuildPath.parentDirectory.parentDirectory.appending(components: "libexec", "swift", "pm", "swiftpm-xctest-helper") + let maybePath = swiftBuildPath.parentDirectory.parentDirectory.appending( + components: "libexec", "swift", "pm", "swiftpm-xctest-helper" + ) if swiftCommandState.fileSystem.isFile(maybePath) { return maybePath } else { @@ -56,7 +58,10 @@ enum TestingSupport { // This will be true during swiftpm development or when using swift.org toolchains. let xcodePath = try TSCBasic.Process.checkNonZeroExit(args: "/usr/bin/xcode-select", "--print-path").spm_chomp() - let installedSwiftBuildPath = try TSCBasic.Process.checkNonZeroExit(args: "/usr/bin/xcrun", "--find", "swift-build", environment: ["DEVELOPER_DIR": xcodePath]).spm_chomp() + let installedSwiftBuildPath = try TSCBasic.Process.checkNonZeroExit( + args: "/usr/bin/xcrun", "--find", "swift-build", + environment: ["DEVELOPER_DIR": xcodePath] + ).spm_chomp() if let xctestHelperPath = findXCTestHelper(swiftBuildPath: try AbsolutePath(validating: installedSwiftBuildPath)) { return xctestHelperPath } @@ -112,7 +117,7 @@ enum TestingSupport { args = [try Self.xctestHelperPath(swiftCommandState: swiftCommandState).pathString, path.pathString, tempFile.path.pathString] let env = try Self.constructTestEnvironment( toolchain: try swiftCommandState.getTargetToolchain(), - buildParameters: swiftCommandState.buildParametersForTest( + destinationBuildParameters: swiftCommandState.buildParametersForTest( enableCodeCoverage: enableCodeCoverage, shouldSkipBuilding: shouldSkipBuilding, experimentalTestOutput: experimentalTestOutput, @@ -128,7 +133,7 @@ enum TestingSupport { #else let env = try Self.constructTestEnvironment( toolchain: try swiftCommandState.getTargetToolchain(), - buildParameters: swiftCommandState.buildParametersForTest( + destinationBuildParameters: swiftCommandState.buildParametersForTest( enableCodeCoverage: enableCodeCoverage, shouldSkipBuilding: shouldSkipBuilding, library: .xctest @@ -145,7 +150,7 @@ enum TestingSupport { /// Creates the environment needed to test related tools. static func constructTestEnvironment( toolchain: UserToolchain, - buildParameters: BuildParameters, + destinationBuildParameters buildParameters: BuildParameters, sanitizers: [Sanitizer] ) throws -> EnvironmentVariables { var env = EnvironmentVariables.process() diff --git a/Sources/Commands/Utilities/XCTEvents.swift b/Sources/Commands/Utilities/XCTEvents.swift index a264b205e3a..0ceedfce77f 100644 --- a/Sources/Commands/Utilities/XCTEvents.swift +++ b/Sources/Commands/Utilities/XCTEvents.swift @@ -237,12 +237,12 @@ extension TestErrorInfo { extension TestIssue { init(_ issue: XCTIssue) { self.init( - type: .init(destinationBuildParameters: issue.type), + type: .init(defaultBuildParameters: issue.type), compactDescription: issue.compactDescription, detailedDescription: issue.detailedDescription, - associatedError: issue.associatedError.map { .init(destinationBuildParameters: $0) }, - sourceCodeContext: .init(destinationBuildParameters: issue.sourceCodeContext), - attachments: issue.attachments.map { .init(destinationBuildParameters: $0) } + associatedError: issue.associatedError.map { .init(defaultBuildParameters: $0) }, + sourceCodeContext: .init(defaultBuildParameters: issue.sourceCodeContext), + attachments: issue.attachments.map { .init(defaultBuildParameters: $0) } ) } } @@ -275,8 +275,8 @@ extension TestLocation { extension TestSourceCodeContext { init(_ context: XCTSourceCodeContext) { self.init( - callStack: context.callStack.map { .init(destinationBuildParameters: $0) }, - location: context.location.map { .init(destinationBuildParameters: $0) } + callStack: context.callStack.map { .init(defaultBuildParameters: $0) }, + location: context.location.map { .init(defaultBuildParameters: $0) } ) } } @@ -285,8 +285,8 @@ extension TestSourceCodeFrame { init(_ frame: XCTSourceCodeFrame) { self.init( address: frame.address, - symbolInfo: (try? frame.symbolInfo()).map { .init(destinationBuildParameters: $0) }, - symbolicationError: frame.symbolicationError.map { .init(destinationBuildParameters: $0) } + symbolInfo: (try? frame.symbolInfo()).map { .init(defaultBuildParameters: $0) }, + symbolicationError: frame.symbolicationError.map { .init(defaultBuildParameters: $0) } ) } } @@ -296,7 +296,7 @@ extension TestSourceCodeSymbolInfo { self.init( imageName: symbolInfo.imageName, symbolName: symbolInfo.symbolName, - location: symbolInfo.location.map { .init(destinationBuildParameters: $0) } + location: symbolInfo.location.map { .init(defaultBuildParameters: $0) } ) } } diff --git a/Sources/PackageGraph/BuildTriple.swift b/Sources/PackageGraph/BuildTriple.swift index 4e121a2c7bb..87d2daf21f1 100644 --- a/Sources/PackageGraph/BuildTriple.swift +++ b/Sources/PackageGraph/BuildTriple.swift @@ -10,6 +10,9 @@ // //===----------------------------------------------------------------------===// +import class PackageModel.Target +import class PackageModel.Product + /// Triple for which code should be compiled for. /// > Note: We're not using "host" and "target" triple terminology in this enum, as that clashes with build /// > system "targets" and can lead to confusion in this context. @@ -20,3 +23,23 @@ public enum BuildTriple { /// Triple of the destination platform for which end products are compiled (the target triple). case destination } + +extension Target { + var buildTriple: BuildTriple { + if self.type == .macro || self.type == .plugin { + .tools + } else { + .destination + } + } +} + +extension Product { + var buildTriple: BuildTriple { + if self.type == .macro || self.type == .plugin { + .tools + } else { + .destination + } + } +} diff --git a/Sources/PackageGraph/CMakeLists.txt b/Sources/PackageGraph/CMakeLists.txt index 6a1695a0a4d..b95b1eb9feb 100644 --- a/Sources/PackageGraph/CMakeLists.txt +++ b/Sources/PackageGraph/CMakeLists.txt @@ -35,7 +35,7 @@ add_library(PackageGraph Resolution/PlatformVersionProvider.swift Resolution/ResolvedPackage.swift Resolution/ResolvedProduct.swift - Resolution/ResolvedTarget.swift + Resolution/ResolvedModule.swift Version+Extensions.swift VersionSetSpecifier.swift) target_link_libraries(PackageGraph PUBLIC diff --git a/Sources/PackageGraph/ModulesGraph+Loading.swift b/Sources/PackageGraph/ModulesGraph+Loading.swift index 716ff676695..1079ad868b0 100644 --- a/Sources/PackageGraph/ModulesGraph+Loading.swift +++ b/Sources/PackageGraph/ModulesGraph+Loading.swift @@ -165,12 +165,12 @@ extension ModulesGraph { observabilityScope: observabilityScope ) - let rootPackages = resolvedPackages.filter{ root.manifests.values.contains($0.manifest) } + let rootPackages = resolvedPackages.filter { root.manifests.values.contains($0.manifest) } checkAllDependenciesAreUsed(rootPackages, observabilityScope: observabilityScope) return try ModulesGraph( rootPackages: rootPackages, - rootDependencies: resolvedPackages.filter{ rootDependencies.contains($0.manifest) }, + rootDependencies: resolvedPackages.filter { rootDependencies.contains($0.manifest) }, dependencies: requiredDependencies, binaryArtifacts: binaryArtifacts ) @@ -180,16 +180,16 @@ extension ModulesGraph { private func checkAllDependenciesAreUsed(_ rootPackages: [ResolvedPackage], observabilityScope: ObservabilityScope) { for package in rootPackages { // List all dependency products dependent on by the package targets. - let productDependencies = IdentifiableSet(package.targets.flatMap({ target in - return target.dependencies.compactMap({ targetDependency in + let productDependencies = IdentifiableSet(package.targets.flatMap { target in + return target.dependencies.compactMap { targetDependency in switch targetDependency { case .product(let product, _): return product case .target: return nil } - }) - })) + } + }) for dependency in package.dependencies { // We continue if the dependency contains executable products to make sure we don't @@ -217,7 +217,12 @@ private func checkAllDependenciesAreUsed(_ rootPackages: [ResolvedPackage], obse ) // Otherwise emit a warning if none of the dependency package's products are used. - let dependencyIsUsed = dependency.products.contains(where: { productDependencies.contains(id: $0.id) }) + let dependencyIsUsed = dependency.products.contains { product in + // Don't compare by product ID, but by product name to make sure both build triples as properties of + // `ResolvedProduct.ID` are allowed. + productDependencies.contains { $0.name == product.name } + } + if !dependencyIsUsed && !observabilityScope.errorsReportedInAnyScope { packageDiagnosticsScope.emit(.unusedDependency(dependency.identity.description)) } @@ -275,7 +280,10 @@ private func createResolvedPackages( // Resolve module aliases, if specified, for targets and their dependencies // across packages. Aliasing will result in target renaming. - let moduleAliasingUsed = try resolveModuleAliases(packageBuilders: packageBuilders, observabilityScope: observabilityScope) + let moduleAliasingUsed = try resolveModuleAliases( + packageBuilders: packageBuilders, + observabilityScope: observabilityScope + ) // Scan and validate the dependencies for packageBuilder in packageBuilders { @@ -640,12 +648,12 @@ private func createResolvedPackages( observabilityScope.emit( ModuleError.duplicateModule( targetName: entry.key, - packages: entry.value.map{ $0.identity }) + packages: entry.value.map { $0.identity }) ) } } - return try packageBuilders.map{ try $0.construct() } + return try packageBuilders.map { try $0.construct() } } private func emitDuplicateProductDiagnostic( @@ -956,7 +964,7 @@ private final class ResolvedTargetBuilder: ResolvedBuilder { } } - return ResolvedTarget( + return ResolvedModule( packageIdentity: self.packageIdentity, underlying: self.target, dependencies: dependencies, diff --git a/Sources/PackageGraph/ModulesGraph.swift b/Sources/PackageGraph/ModulesGraph.swift index fdf206d84e5..1e5b0bb9690 100644 --- a/Sources/PackageGraph/ModulesGraph.swift +++ b/Sources/PackageGraph/ModulesGraph.swift @@ -92,17 +92,16 @@ public struct ModulesGraph { public let packages: [ResolvedPackage] /// The list of all targets reachable from root targets. - public let reachableTargets: IdentifiableSet + public private(set) var reachableTargets: IdentifiableSet /// The list of all products reachable from root targets. - public let reachableProducts: IdentifiableSet + public private(set) var reachableProducts: IdentifiableSet /// Returns all the targets in the graph, regardless if they are reachable from the root targets or not. - public let allTargets: IdentifiableSet + public private(set) var allTargets: IdentifiableSet /// Returns all the products in the graph, regardless if they are reachable from the root targets or not. - - public let allProducts: IdentifiableSet + public private(set) var allProducts: IdentifiableSet /// Package dependencies required for a fully resolved graph. /// @@ -113,10 +112,14 @@ public struct ModulesGraph { /// Returns true if a given target is present in root packages and is not excluded for the given build environment. public func isInRootPackages(_ target: ResolvedModule, satisfying buildEnvironment: BuildEnvironment) -> Bool { // FIXME: This can be easily cached. - return rootPackages.reduce(into: IdentifiableSet()) { (accumulator: inout IdentifiableSet, package: ResolvedPackage) in + return rootPackages.reduce( + into: IdentifiableSet() + ) { (accumulator: inout IdentifiableSet, package: ResolvedPackage) in let allDependencies = package.targets.flatMap { $0.dependencies } let unsatisfiedDependencies = allDependencies.filter { !$0.satisfies(buildEnvironment) } - let unsatisfiedDependencyTargets = unsatisfiedDependencies.compactMap { (dep: ResolvedModule.Dependency) -> ResolvedModule? in + let unsatisfiedDependencyTargets = unsatisfiedDependencies.compactMap { ( + dep: ResolvedModule.Dependency + ) -> ResolvedModule? in switch dep { case .target(let target, _): return target @@ -134,14 +137,14 @@ public struct ModulesGraph { return self.rootPackages.contains(id: package.id) } - private let modulesToPackages: [ResolvedModule.ID: ResolvedPackage] + private var modulesToPackages: [ResolvedModule.ID: ResolvedPackage] /// Returns the package that contains the module, or nil if the module isn't in the graph. public func package(for module: ResolvedModule) -> ResolvedPackage? { return self.modulesToPackages[module.id] } - private let productsToPackages: [ResolvedProduct.ID: ResolvedPackage] + private var productsToPackages: [ResolvedProduct.ID: ResolvedPackage] /// Returns the package that contains the product, or nil if the product isn't in the graph. public func package(for product: ResolvedProduct) -> ResolvedPackage? { return self.productsToPackages[product.id] @@ -165,44 +168,68 @@ public struct ModulesGraph { self.inputPackages = rootPackages + rootDependencies self.binaryArtifacts = binaryArtifacts self.packages = try topologicalSort(inputPackages, successors: { $0.dependencies }) + let identitiesToPackages = self.packages.spm_createDictionary { ($0.identity, $0) } // Create a mapping from targets to the packages that define them. Here // we include all targets, including tests in non-root packages, since // this is intended for lookup and not traversal. - self.modulesToPackages = packages.reduce(into: [:], { partial, package in - package.targets.forEach{ partial[$0.id] = package } + var modulesToPackages = self.packages.reduce(into: [:], { partial, package in + package.targets.forEach { partial[$0.id] = package } + }) + + // Create a mapping from products to the packages that define them. Here + // we include all products, including tests in non-root packages, since + // this is intended for lookup and not traversal. + var productsToPackages = packages.reduce(into: [:], { partial, package in + package.products.forEach { partial[$0.id] = package } }) - let allTargets = IdentifiableSet(packages.flatMap({ package -> [ResolvedModule] in + var allTargets = IdentifiableSet() + var allProducts = IdentifiableSet() + for package in self.packages { + let targetsToInclude: [ResolvedModule] if rootPackages.contains(id: package.id) { - return package.targets + targetsToInclude = Array(package.targets) } else { // Don't include tests targets from non-root packages so swift-test doesn't // try to run them. - return package.targets.filter({ $0.type != .test }) + targetsToInclude = package.targets.filter { $0.type != .test } } - })) - // Create a mapping from products to the packages that define them. Here - // we include all products, including tests in non-root packages, since - // this is intended for lookup and not traversal. - self.productsToPackages = packages.reduce(into: [:], { partial, package in - package.products.forEach { partial[$0.id] = package } - }) + for target in targetsToInclude { + allTargets.insert(target) + + // Explicitly include dependencies of host tools in the maps of all targets or all products + if target.buildTriple == .tools { + for dependency in try target.recursiveDependencies() { + switch dependency { + case .target(let targetDependency, _): + allTargets.insert(targetDependency) + modulesToPackages[targetDependency.id] = package + case .product(let productDependency, _): + allProducts.insert(productDependency) + productsToPackages[productDependency.id] = + identitiesToPackages[productDependency.packageIdentity] + } + } + } + } - let allProducts = IdentifiableSet(packages.flatMap({ package -> [ResolvedProduct] in if rootPackages.contains(id: package.id) { - return package.products + allProducts.formUnion(package.products) } else { - // Don't include tests products from non-root packages so swift-test doesn't + // Don't include test products from non-root packages so swift-test doesn't // try to run them. - return package.products.filter({ $0.type != .test }) + allProducts.formUnion(package.products.filter { $0.type != .test }) } - })) + } + + self.modulesToPackages = modulesToPackages + self.productsToPackages = productsToPackages // Compute the reachable targets and products. - let inputTargets = inputPackages.flatMap { $0.targets } - let inputProducts = inputPackages.flatMap { $0.products } + let inputTargets = self.inputPackages.flatMap { $0.targets } + let inputProducts = self.inputPackages.flatMap { $0.products } let recursiveDependencies = try inputTargets.lazy.flatMap { try $0.recursiveDependencies() } self.reachableTargets = IdentifiableSet(inputTargets).union(recursiveDependencies.compactMap { $0.target }) @@ -212,6 +239,41 @@ public struct ModulesGraph { self.allProducts = allProducts } + package mutating func updateBuildTripleRecursively(_ buildTriple: BuildTriple) throws { + self.reachableTargets = IdentifiableSet(self.reachableTargets.map { + var target = $0 + target.buildTriple = buildTriple + return target + }) + self.reachableProducts = IdentifiableSet(self.reachableProducts.map { + var product = $0 + product.buildTriple = buildTriple + return product + }) + + self.allTargets = IdentifiableSet(self.allTargets.map { + var target = $0 + target.buildTriple = buildTriple + return target + }) + self.allProducts = IdentifiableSet(self.allProducts.map { + var product = $0 + product.buildTriple = buildTriple + return product + }) + + self.modulesToPackages = .init(self.modulesToPackages.map { + var target = $0 + target.buildTriple = buildTriple + return (target, $1) + }, uniquingKeysWith: { $1 }) + self.productsToPackages = .init(self.productsToPackages.map { + var product = $0 + product.buildTriple = buildTriple + return (product, $1) + }, uniquingKeysWith: { $1 }) + } + /// Computes a map from each executable target in any of the root packages to the corresponding test targets. @_spi(SwiftPMInternal) public func computeTestTargetsForExecutableTargets() throws -> [ResolvedModule.ID: [ResolvedModule]] { diff --git a/Sources/PackageGraph/Resolution/ResolvedTarget.swift b/Sources/PackageGraph/Resolution/ResolvedModule.swift similarity index 78% rename from Sources/PackageGraph/Resolution/ResolvedTarget.swift rename to Sources/PackageGraph/Resolution/ResolvedModule.swift index b8854a8ba3b..f218a35886d 100644 --- a/Sources/PackageGraph/Resolution/ResolvedTarget.swift +++ b/Sources/PackageGraph/Resolution/ResolvedModule.swift @@ -136,7 +136,7 @@ public struct ResolvedModule { public let underlying: Target /// The dependencies of this target. - public let dependencies: [Dependency] + public internal(set) var dependencies: [Dependency] /// The default localization for resources. public let defaultLocalization: String? @@ -147,7 +147,11 @@ public struct ResolvedModule { private let platformVersionProvider: PlatformVersionProvider /// Triple for which this resolved target should be compiled for. - public let buildTriple: BuildTriple + public package(set) var buildTriple: BuildTriple { + didSet { + self.updateBuildTriplesOfDependencies() + } + } /// Create a resolved target instance. public init( @@ -164,7 +168,50 @@ public struct ResolvedModule { self.defaultLocalization = defaultLocalization self.supportedPlatforms = supportedPlatforms self.platformVersionProvider = platformVersionProvider - self.buildTriple = .destination + + if underlying.type == .test { + // Make sure that test products are built for the tools triple if it has tools as direct dependencies. + // Without this workaround, `assertMacroExpansion` in tests can't be built, as it requires macros + // and SwiftSyntax to be built for the same triple as the tests. + // See https://github.com/apple/swift-package-manager/pull/7349 for more context. + var inferredBuildTriple = BuildTriple.destination + loop: for dependency in dependencies { + switch dependency { + case .target(let targetDependency, _): + if targetDependency.type == .macro { + inferredBuildTriple = .tools + break loop + } + case .product(let productDependency, _): + if productDependency.type == .macro { + inferredBuildTriple = .tools + break loop + } + } + } + self.buildTriple = inferredBuildTriple + } else { + self.buildTriple = underlying.buildTriple + } + self.updateBuildTriplesOfDependencies() + } + + mutating func updateBuildTriplesOfDependencies() { + if self.buildTriple == .tools { + for (i, dependency) in dependencies.enumerated() { + let updatedDependency: Dependency + switch dependency { + case .target(var target, let conditions): + target.buildTriple = self.buildTriple + updatedDependency = .target(target, conditions: conditions) + case .product(var product, let conditions): + product.buildTriple = self.buildTriple + updatedDependency = .product(product, conditions: conditions) + } + + dependencies[i] = updatedDependency + } + } } public func getSupportedPlatform(for platform: Platform, usingXCTest: Bool) -> SupportedPlatform { @@ -178,13 +225,13 @@ public struct ResolvedModule { extension ResolvedModule: CustomStringConvertible { public var description: String { - return "" + return "" } } extension ResolvedModule.Dependency: CustomStringConvertible { public var description: String { - var str = " + public internal(set) var targets: IdentifiableSet /// Executable target for test entry point file. public let testEntryPointTarget: ResolvedModule? @@ -44,7 +44,11 @@ public struct ResolvedProduct { public let platformVersionProvider: PlatformVersionProvider /// Triple for which this resolved product should be compiled for. - public let buildTriple: BuildTriple + public internal(set) var buildTriple: BuildTriple { + didSet { + self.updateBuildTriplesOfDependencies() + } + } /// The main executable target of product. /// @@ -63,7 +67,11 @@ public struct ResolvedProduct { } } - public init(packageIdentity: PackageIdentity, product: Product, targets: IdentifiableSet) { + public init( + packageIdentity: PackageIdentity, + product: Product, + targets: IdentifiableSet + ) { assert(product.targets.count == targets.count && product.targets.map(\.name).sorted() == targets.map(\.name).sorted()) self.packageIdentity = packageIdentity self.underlying = product @@ -97,7 +105,41 @@ public struct ResolvedProduct { ) } - self.buildTriple = .destination + if product.type == .test { + // Make sure that test products are built for the tools triple if it has tools as direct dependencies. + // Without this workaround, `assertMacroExpansion` in tests can't be built, as it requires macros + // and SwiftSyntax to be built for the same triple as the tests. + // See https://github.com/apple/swift-package-manager/pull/7349 for more context. + var inferredBuildTriple = BuildTriple.destination + targetsLoop: for target in targets { + for dependency in target.dependencies { + switch dependency { + case .target(let targetDependency, _): + if targetDependency.type == .macro { + inferredBuildTriple = .tools + break targetsLoop + } + case .product(let productDependency, _): + if productDependency.type == .macro { + inferredBuildTriple = .tools + break targetsLoop + } + } + } + } + self.buildTriple = inferredBuildTriple + } else { + self.buildTriple = product.buildTriple + } + self.updateBuildTriplesOfDependencies() + } + + mutating func updateBuildTriplesOfDependencies() { + self.targets = IdentifiableSet(self.targets.map { + var target = $0 + target.buildTriple = self.buildTriple + return target + }) } /// True if this product contains Swift targets. @@ -151,7 +193,7 @@ public struct ResolvedProduct { extension ResolvedProduct: CustomStringConvertible { public var description: String { - "" + "" } } @@ -166,13 +208,13 @@ extension ResolvedProduct { extension ResolvedProduct: Identifiable { /// Resolved target identity that uniquely identifies it in a resolution graph. public struct ID: Hashable { - public let targetName: String + public let productName: String let packageIdentity: PackageIdentity - public let buildTriple: BuildTriple + public var buildTriple: BuildTriple } public var id: ID { - ID(targetName: self.name, packageIdentity: self.packageIdentity, buildTriple: self.buildTriple) + ID(productName: self.name, packageIdentity: self.packageIdentity, buildTriple: self.buildTriple) } } diff --git a/Sources/PackageModel/Target/Target.swift b/Sources/PackageModel/Target/Target.swift index 6ab22ca7b26..40bb1a575e9 100644 --- a/Sources/PackageModel/Target/Target.swift +++ b/Sources/PackageModel/Target/Target.swift @@ -131,7 +131,7 @@ public class Target: PolymorphicCodableProtocol { /// The name of the target. /// /// NOTE: This name is not the language-level target (i.e., the importable - /// name) name in many cases, instead use c99name if you need uniqueness. + /// name) name in many cases, instead use ``Target/c99name`` if you need uniqueness. public private(set) var name: String /// Module aliases needed to build this target. The key is an original name of a diff --git a/Sources/SPMBuildCore/BuildParameters/BuildParameters.swift b/Sources/SPMBuildCore/BuildParameters/BuildParameters.swift index e81e99e242f..02902339324 100644 --- a/Sources/SPMBuildCore/BuildParameters/BuildParameters.swift +++ b/Sources/SPMBuildCore/BuildParameters/BuildParameters.swift @@ -248,7 +248,7 @@ public struct BuildParameters: Encodable { /// Returns the path to the binary of a product for the current build parameters, relative to the build directory. public func binaryRelativePath(for product: ResolvedProduct) throws -> RelativePath { - let potentialExecutablePath = try RelativePath(validating: "\(product.name)\(self.triple.executableExtension)") + let potentialExecutablePath = try RelativePath(validating: "\(product.name)\(self.suffix(triple: product.buildTriple))\(self.triple.executableExtension)") switch product.type { case .executable, .snippet: @@ -329,3 +329,11 @@ extension Triple { return !self.isWindows() } } + +extension BuildParameters { + /// Suffix appended to build manifest nodes to distinguish nodes created for tools from nodes created for + /// end products, i.e. nodes for host vs target triples. + package func suffix(triple: BuildTriple) -> String { + if triple == .tools { "-tool" } else { "" } + } +} diff --git a/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift b/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift index f8281b25146..fc52987ce01 100644 --- a/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift +++ b/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift @@ -74,7 +74,7 @@ extension ProductBuildDescription { /// The path to the product binary produced. public var binaryPath: AbsolutePath { get throws { - return try self.buildParameters.binaryPath(for: product) + try self.buildParameters.binaryPath(for: product) } } } diff --git a/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift b/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift index 4352b35ffac..9edfb0a7bb4 100644 --- a/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift +++ b/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift @@ -55,7 +55,7 @@ internal struct PluginContextSerializer { // Adds a target to the serialized structure, if it isn't already there and // if it is of a kind that should be passed to the plugin. If so, this func- // tion returns the target's wire ID. If not, it returns nil. - mutating func serialize(target: ResolvedTarget) throws -> WireInput.Target.Id? { + mutating func serialize(target: ResolvedModule) throws -> WireInput.Target.Id? { // If we've already seen the target, just return the wire ID we already assigned to it. if let id = targetsToWireIDs[target.id] { return id } diff --git a/Sources/SPMBuildCore/Plugins/PluginInvocation.swift b/Sources/SPMBuildCore/Plugins/PluginInvocation.swift index 9fb03674540..fc832b07dc4 100644 --- a/Sources/SPMBuildCore/Plugins/PluginInvocation.swift +++ b/Sources/SPMBuildCore/Plugins/PluginInvocation.swift @@ -398,8 +398,8 @@ extension ModulesGraph { observabilityScope: ObservabilityScope, fileSystem: FileSystem, builtToolHandler: (_ name: String, _ path: RelativePath) throws -> AbsolutePath? = { _, _ in return nil } - ) throws -> [ResolvedTarget.ID: (target: ResolvedTarget, results: [BuildToolPluginInvocationResult])] { - var pluginResultsByTarget: [ResolvedTarget.ID: (target: ResolvedTarget, results: [BuildToolPluginInvocationResult])] = [:] + ) throws -> [ResolvedModule.ID: (target: ResolvedModule, results: [BuildToolPluginInvocationResult])] { + var pluginResultsByTarget: [ResolvedModule.ID: (target: ResolvedModule, results: [BuildToolPluginInvocationResult])] = [:] for target in self.allTargets.sorted(by: { $0.name < $1.name }) { // Infer plugins from the declared dependencies, and collect them as well as any regular dependencies. Although usage of build tool plugins is declared separately from dependencies in the manifest, in the internal model we currently consider both to be dependencies. var pluginTargets: [PluginTarget] = [] @@ -592,13 +592,15 @@ extension ModulesGraph { } // Associate the list of results with the target. The list will have one entry for each plugin used by the target. - pluginResultsByTarget[target.id] = (target, buildToolPluginResults) + var targetID = target.id + targetID.buildTriple = .destination + pluginResultsByTarget[targetID] = (target, buildToolPluginResults) } return pluginResultsByTarget } public static func computePluginGeneratedFiles( - target: ResolvedTarget, + target: ResolvedModule, toolsVersion: ToolsVersion, additionalFileRules: [FileRuleDescription], buildParameters: BuildParameters, @@ -744,7 +746,7 @@ public struct BuildToolPluginInvocationResult { public var package: ResolvedPackage /// The target in that package to which the plugin was applied. - public var target: ResolvedTarget + public var target: ResolvedModule /// If the plugin finished successfully. public var succeeded: Bool diff --git a/Sources/SPMTestSupport/MockArchiver.swift b/Sources/SPMTestSupport/MockArchiver.swift index 209bf9bab00..cfc57d3f2d7 100644 --- a/Sources/SPMTestSupport/MockArchiver.swift +++ b/Sources/SPMTestSupport/MockArchiver.swift @@ -12,7 +12,7 @@ import Basics -package class MockArchiver: Archiver { +package final class MockArchiver: Archiver { package typealias ExtractionHandler = ( MockArchiver, AbsolutePath, diff --git a/Sources/SPMTestSupport/MockBuildTestHelper.swift b/Sources/SPMTestSupport/MockBuildTestHelper.swift index bce133aacdb..1b8824b928c 100644 --- a/Sources/SPMTestSupport/MockBuildTestHelper.swift +++ b/Sources/SPMTestSupport/MockBuildTestHelper.swift @@ -14,6 +14,8 @@ import Basics import Build +import struct PackageGraph.ResolvedModule +import struct PackageGraph.ResolvedProduct import PackageModel import SPMBuildCore import TSCUtility @@ -74,14 +76,14 @@ package let defaultTargetTriple: String = hostTriple.tripleString #endif package func mockBuildParameters( - buildPath: AbsolutePath = "/path/to/build", + buildPath: AbsolutePath? = nil, config: BuildConfiguration = .debug, toolchain: PackageModel.Toolchain = MockToolchain(), flags: PackageModel.BuildFlags = PackageModel.BuildFlags(), shouldLinkStaticSwiftStdlib: Bool = false, shouldDisableLocalRpath: Bool = false, canRenameEntrypointFunctionName: Bool = false, - targetTriple: Basics.Triple = hostTriple, + triple: Basics.Triple = hostTriple, indexStoreMode: BuildParameters.IndexStoreMode = .off, useExplicitModuleBuild: Bool = false, linkerDeadStrip: Bool = true, @@ -89,16 +91,16 @@ package func mockBuildParameters( omitFramePointers: Bool? = nil ) -> BuildParameters { try! BuildParameters( - dataPath: buildPath, + dataPath: buildPath ?? AbsolutePath("/path/to/build").appending(triple.tripleString), configuration: config, toolchain: toolchain, - triple: targetTriple, + triple: triple, flags: flags, pkgConfigDirectories: [], workers: 3, indexStoreMode: indexStoreMode, debuggingParameters: .init( - triple: targetTriple, + triple: triple, shouldEnableDebuggingEntitlement: config == .debug, omitFramePointers: omitFramePointers ), @@ -130,7 +132,7 @@ package func mockBuildParameters(environment: BuildEnvironment) -> BuildParamete fatalError("unsupported platform in tests") } - return mockBuildParameters(config: environment.configuration ?? .debug, targetTriple: triple) + return mockBuildParameters(config: environment.configuration ?? .debug, triple: triple) } enum BuildError: Swift.Error { @@ -139,15 +141,15 @@ enum BuildError: Swift.Error { package struct BuildPlanResult { package let plan: Build.BuildPlan - package let targetMap: [String: TargetBuildDescription] - package let productMap: [String: Build.ProductBuildDescription] + package let targetMap: [ResolvedModule.ID: TargetBuildDescription] + package let productMap: [ResolvedProduct.ID: Build.ProductBuildDescription] package init(plan: Build.BuildPlan) throws { self.plan = plan self.productMap = try Dictionary( throwingUniqueKeysWithValues: plan.buildProducts .compactMap { $0 as? Build.ProductBuildDescription } - .map { ($0.product.name, $0) } + .map { ($0.product.id, $0) } ) self.targetMap = try Dictionary( throwingUniqueKeysWithValues: plan.targetMap.compactMap { @@ -157,7 +159,7 @@ package struct BuildPlanResult { else { throw BuildError.error("Target \($0) not found.") } - return (target.name, $1) + return (target.id, $1) } ) } @@ -171,16 +173,26 @@ package struct BuildPlanResult { } package func target(for name: String) throws -> TargetBuildDescription { - guard let target = targetMap[name] else { - throw BuildError.error("Target \(name) not found.") + let matchingIDs = targetMap.keys.filter({ $0.targetName == name }) + guard matchingIDs.count == 1, let target = targetMap[matchingIDs[0]] else { + if matchingIDs.isEmpty { + throw BuildError.error("Target \(name) not found.") + } else { + throw BuildError.error("More than one target \(name) found.") + } } return target } package func buildProduct(for name: String) throws -> Build.ProductBuildDescription { - guard let product = productMap[name] else { - // Display the thrown error on macOS - throw BuildError.error("Product \(name) not found.") + let matchingIDs = productMap.keys.filter({ $0.productName == name }) + guard matchingIDs.count == 1, let product = productMap[matchingIDs[0]] else { + if matchingIDs.isEmpty { + // Display the thrown error on macOS + throw BuildError.error("Product \(name) not found.") + } else { + throw BuildError.error("More than one target \(name) found.") + } } return product } diff --git a/Sources/SPMTestSupport/MockPackageGraphs.swift b/Sources/SPMTestSupport/MockPackageGraphs.swift index 3f7b77ec353..1f3f956e23a 100644 --- a/Sources/SPMTestSupport/MockPackageGraphs.swift +++ b/Sources/SPMTestSupport/MockPackageGraphs.swift @@ -39,7 +39,7 @@ package func macrosPackageGraph() throws -> MockPackageGraph { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -124,6 +124,135 @@ package func macrosPackageGraph() throws -> MockPackageGraph { return (graph, fs, observability.topScope) } +package func macrosTestsPackageGraph() throws -> MockPackageGraph { + let fs = InMemoryFileSystem(emptyFiles: + "/swift-mmio/Sources/MMIO/source.swift", + "/swift-mmio/Sources/MMIOMacros/source.swift", + "/swift-mmio/Sources/MMIOMacrosTests/source.swift", + "/swift-syntax/Sources/SwiftSyntax/source.swift", + "/swift-syntax/Sources/SwiftSyntaxMacrosTestSupport/source.swift", + "/swift-syntax/Sources/SwiftSyntaxMacros/source.swift", + "/swift-syntax/Sources/SwiftCompilerPlugin/source.swift", + "/swift-syntax/Sources/SwiftCompilerPluginMessageHandling/source.swift", + "/swift-syntax/Tests/SwiftSyntaxTests/source.swift" + ) + + let observability = ObservabilitySystem.makeForTesting() + let graph = try loadModulesGraph( + fileSystem: fs, + manifests: [ + Manifest.createRootManifest( + displayName: "swift-mmio", + path: "/swift-mmio", + dependencies: [ + .localSourceControl( + path: "/swift-syntax", + requirement: .upToNextMajor(from: "1.0.0") + ) + ], + products: [ + ProductDescription( + name: "MMIO", + type: .library(.automatic), + targets: ["MMIO"] + ) + ], + targets: [ + TargetDescription( + name: "MMIO", + dependencies: [.target(name: "MMIOMacros")] + ), + TargetDescription( + name: "MMIOMacros", + dependencies: [ + .product(name: "SwiftSyntaxMacros", package: "swift-syntax"), + .product(name: "SwiftCompilerPlugin", package: "swift-syntax"), + ], + type: .macro + ), + TargetDescription( + name: "MMIOMacrosTests", + dependencies: [ + .target(name: "MMIOMacros"), + .product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax") + ], + type: .test + ) + ] + ), + Manifest.createFileSystemManifest( + displayName: "swift-syntax", + path: "/swift-syntax", + products: [ + ProductDescription( + name: "SwiftSyntaxMacros", + type: .library(.automatic), + targets: ["SwiftSyntax"] + ), + ProductDescription( + name: "SwiftSyntax", + type: .library(.automatic), + targets: ["SwiftSyntax"] + ), + ProductDescription( + name: "SwiftSyntaxMacrosTestSupport", + type: .library(.automatic), + targets: ["SwiftSyntaxMacrosTestSupport"] + ), + ProductDescription( + name: "SwiftCompilerPlugin", + type: .library(.automatic), + targets: ["SwiftCompilerPlugin"] + ), + ProductDescription( + name: "SwiftCompilerPluginMessageHandling", + type: .library(.automatic), + targets: ["SwiftCompilerPluginMessageHandling"] + ), + ], + targets: [ + TargetDescription( + name: "SwiftSyntax", + dependencies: [] + ), + TargetDescription( + name: "SwiftSyntaxMacros", + dependencies: [.target(name: "SwiftSyntax")] + ), + TargetDescription( + name: "SwiftCompilerPlugin", + dependencies: [ + .target(name: "SwiftCompilerPluginMessageHandling"), + .target(name: "SwiftSyntaxMacros"), + ] + ), + TargetDescription( + name: "SwiftCompilerPluginMessageHandling", + dependencies: [ + .target(name: "SwiftSyntax"), + .target(name: "SwiftSyntaxMacros"), + ] + ), + TargetDescription( + name: "SwiftSyntaxMacrosTestSupport", + dependencies: [.target(name: "SwiftSyntax")] + ), + TargetDescription( + name: "SwiftSyntaxTests", + dependencies: ["SwiftSyntax"], + type: .test + ), + ] + ), + ], + observabilityScope: observability.topScope + ) + + XCTAssertNoDiagnostics(observability.diagnostics) + + return (graph, fs, observability.topScope) +} + package func trivialPackageGraph(pkgRootPath: AbsolutePath) throws -> MockPackageGraph { let fs = InMemoryFileSystem( emptyFiles: @@ -134,7 +263,7 @@ package func trivialPackageGraph(pkgRootPath: AbsolutePath) throws -> MockPackag ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -164,7 +293,7 @@ package func embeddedCxxInteropPackageGraph(pkgRootPath: AbsolutePath) throws -> ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( diff --git a/Sources/SPMTestSupport/PackageGraphTester.swift b/Sources/SPMTestSupport/PackageGraphTester.swift index 7a40c5a6d3f..a45ee9d57ad 100644 --- a/Sources/SPMTestSupport/PackageGraphTester.swift +++ b/Sources/SPMTestSupport/PackageGraphTester.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2014-2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information @@ -17,8 +17,8 @@ import struct Basics.IdentifiableSet import PackageModel import PackageGraph -package func PackageGraphTester(_ graph: ModulesGraph, _ result: (PackageGraphResult) -> Void) { - result(PackageGraphResult(graph)) +package func PackageGraphTester(_ graph: ModulesGraph, _ result: (PackageGraphResult) throws -> Void) rethrows { + try result(PackageGraphResult(graph)) } package final class PackageGraphResult { @@ -49,8 +49,8 @@ package final class PackageGraphResult { package func check(targets: String..., file: StaticString = #file, line: UInt = #line) { XCTAssertEqual( graph.allTargets - .filter{ $0.type != .test } - .map{ $0.name } + .filter { $0.type != .test } + .map { $0.name } .sorted(), targets.sorted(), file: file, line: line) } @@ -92,10 +92,24 @@ package final class PackageGraphResult { line: UInt = #line, body: (ResolvedTargetResult) -> Void ) { - guard let target = find(target: name) else { + let targets = find(target: name) + + guard targets.count > 0 else { return XCTFail("Target \(name) not found", file: file, line: line) } - body(ResolvedTargetResult(target)) + guard targets.count == 1 else { + return XCTFail("More than a single target with name \(name) found", file: file, line: line) + } + body(ResolvedTargetResult(targets[0])) + } + + package func checkTargets( + _ name: String, + file: StaticString = #file, + line: UInt = #line, + body: ([ResolvedTargetResult]) throws -> Void + ) rethrows { + try body(graph.allTargets.filter { $0.name == name }.map(ResolvedTargetResult.init)) } package func checkProduct( @@ -104,10 +118,16 @@ package final class PackageGraphResult { line: UInt = #line, body: (ResolvedProductResult) -> Void ) { - guard let target = find(product: name) else { + let products = find(product: name) + + guard products.count > 0 else { return XCTFail("Product \(name) not found", file: file, line: line) } - body(ResolvedProductResult(target)) + + guard products.count == 1 else { + return XCTFail("More than a single product with name \(name) found", file: file, line: line) + } + body(ResolvedProductResult(products[0])) } package func check(testModules: String..., file: StaticString = #file, line: UInt = #line) { @@ -118,19 +138,19 @@ package final class PackageGraphResult { .sorted(), testModules.sorted(), file: file, line: line) } - package func find(target: String) -> ResolvedTarget? { - return graph.allTargets.first(where: { $0.name == target }) + package func find(target: String) -> [ResolvedModule] { + return graph.allTargets.filter { $0.name == target } } - package func find(product: String) -> ResolvedProduct? { - return graph.allProducts.first(where: { $0.name == product }) + package func find(product: String) -> [ResolvedProduct] { + return graph.allProducts.filter { $0.name == product } } package func find(package: PackageIdentity) -> ResolvedPackage? { return graph.packages.first(where: { $0.identity == package }) } - private func reachableBuildTargets(in environment: BuildEnvironment) throws -> IdentifiableSet { + private func reachableBuildTargets(in environment: BuildEnvironment) throws -> IdentifiableSet { let inputTargets = graph.inputPackages.lazy.flatMap { $0.targets } let recursiveBuildTargetDependencies = try inputTargets .flatMap { try $0.recursiveDependencies(satisfying: environment) } @@ -149,9 +169,9 @@ package final class PackageGraphResult { } package final class ResolvedTargetResult { - private let target: ResolvedTarget + let target: ResolvedModule - init(_ target: ResolvedTarget) { + init(_ target: ResolvedModule) { self.target = target } @@ -159,6 +179,10 @@ package final class ResolvedTargetResult { XCTAssertEqual(Set(dependencies), Set(target.dependencies.map({ $0.name })), file: file, line: line) } + package func check(dependencies: [String], file: StaticString = #file, line: UInt = #line) { + XCTAssertEqual(Set(dependencies), Set(target.dependencies.map({ $0.name })), file: file, line: line) + } + package func checkDependency( _ name: String, file: StaticString = #file, @@ -176,7 +200,9 @@ package final class ResolvedTargetResult { } package func checkDeclaredPlatforms(_ platforms: [String: String], file: StaticString = #file, line: UInt = #line) { - let targetPlatforms = Dictionary(uniqueKeysWithValues: target.supportedPlatforms.map({ ($0.platform.name, $0.version.versionString) })) + let targetPlatforms = Dictionary( + uniqueKeysWithValues: target.supportedPlatforms.map { ($0.platform.name, $0.version.versionString) } + ) XCTAssertEqual(platforms, targetPlatforms, file: file, line: line) } @@ -187,22 +213,30 @@ package final class ResolvedTargetResult { return self.target.getSupportedPlatform(for: platform, usingXCTest: self.target.type == .test) } let targetPlatforms = Dictionary( - uniqueKeysWithValues: derived - .map { ($0.platform.name, $0.version.versionString) } + uniqueKeysWithValues: derived.map { ($0.platform.name, $0.version.versionString) } ) XCTAssertEqual(platforms, targetPlatforms, file: file, line: line) } - package func checkDerivedPlatformOptions(_ platform: PackageModel.Platform, options: [String], file: StaticString = #file, line: UInt = #line) { - let platform = target.getSupportedPlatform(for: platform, usingXCTest: target.type == .test) + package func checkDerivedPlatformOptions( + _ platform: PackageModel.Platform, + options: [String], + file: StaticString = #file, + line: UInt = #line + ) { + let platform = self.target.getSupportedPlatform(for: platform, usingXCTest: target.type == .test) XCTAssertEqual(platform.options, options, file: file, line: line) } + + public func check(buildTriple: BuildTriple, file: StaticString = #file, line: UInt = #line) { + XCTAssertEqual(self.target.buildTriple, buildTriple, file: file, line: line) + } } package final class ResolvedTargetDependencyResult { - private let dependency: ResolvedTarget.Dependency + private let dependency: ResolvedModule.Dependency - init(_ dependency: ResolvedTarget.Dependency) { + init(_ dependency: ResolvedModule.Dependency) { self.dependency = dependency } @@ -217,6 +251,28 @@ package final class ResolvedTargetDependencyResult { ) { XCTAssert(!dependency.conditions.allSatisfy({ $0.satisfies(environment) }), file: file, line: line) } + + public func checkTarget( + file: StaticString = #file, + line: UInt = #line, + body: (ResolvedTargetResult) -> Void + ) { + guard case let .target(target, _) = self.dependency else { + return XCTFail("Dependency \(dependency) is not a target", file: file, line: line) + } + body(ResolvedTargetResult(target)) + } + + public func checkProduct( + file: StaticString = #file, + line: UInt = #line, + body: (ResolvedProductResult) -> Void + ) { + guard case let .product(product, _) = self.dependency else { + return XCTFail("Dependency \(dependency) is not a product", file: file, line: line) + } + body(ResolvedProductResult(product)) + } } package final class ResolvedProductResult { @@ -252,9 +308,25 @@ package final class ResolvedProductResult { let platform = product.getSupportedPlatform(for: platform, usingXCTest: product.isLinkingXCTest) XCTAssertEqual(platform.options, options, file: file, line: line) } + + public func check(buildTriple: BuildTriple, file: StaticString = #file, line: UInt = #line) { + XCTAssertEqual(self.product.buildTriple, buildTriple, file: file, line: line) + } + + public func checkTarget( + _ name: String, + file: StaticString = #file, + line: UInt = #line, + body: (ResolvedTargetResult) -> Void + ) { + guard let target = product.targets.first(where: { $0.name == name }) else { + return XCTFail("Target \(name) not found", file: file, line: line) + } + body(ResolvedTargetResult(target)) + } } -extension ResolvedTarget.Dependency { +extension ResolvedModule.Dependency { package var name: String { switch self { case .target(let target, _): diff --git a/Sources/SPMTestSupport/ResolvedTarget+Mock.swift b/Sources/SPMTestSupport/ResolvedTarget+Mock.swift index 6f109474acf..2a28903dd88 100644 --- a/Sources/SPMTestSupport/ResolvedTarget+Mock.swift +++ b/Sources/SPMTestSupport/ResolvedTarget+Mock.swift @@ -13,14 +13,14 @@ import PackageGraph import PackageModel -extension ResolvedTarget { +extension ResolvedModule { package static func mock( packageIdentity: PackageIdentity, name: String, - deps: ResolvedTarget..., + deps: ResolvedModule..., conditions: [PackageCondition] = [] - ) -> ResolvedTarget { - ResolvedTarget( + ) -> ResolvedModule { + ResolvedModule( packageIdentity: packageIdentity, underlying: SwiftTarget( name: name, diff --git a/Tests/BuildTests/BuildOperationTests.swift b/Tests/BuildTests/BuildOperationTests.swift index 8574f12f198..21c40cafb5d 100644 --- a/Tests/BuildTests/BuildOperationTests.swift +++ b/Tests/BuildTests/BuildOperationTests.swift @@ -28,14 +28,16 @@ import class TSCBasic.InMemoryFileSystem final class BuildOperationTests: XCTestCase { func testDetectUnexpressedDependencies() throws { + let buildParameters = mockBuildParameters(shouldDisableLocalRpath: false) + let fs = InMemoryFileSystem(files: [ - "/path/to/build/debug/Lunch.build/Lunch.d" : "/Best.framework" + "\(buildParameters.dataPath)/debug/Lunch.build/Lunch.d" : "/Best.framework" ]) let observability = ObservabilitySystem.makeForTesting() let buildOp = BuildOperation( - productsBuildParameters: mockBuildParameters(shouldDisableLocalRpath: false), - toolsBuildParameters: mockBuildParameters(shouldDisableLocalRpath: false), + productsBuildParameters: buildParameters, + toolsBuildParameters: buildParameters, cacheBuildManifest: false, packageGraphLoader: { fatalError() }, additionalFileRules: [], diff --git a/Tests/BuildTests/BuildPlanTests.swift b/Tests/BuildTests/BuildPlanTests.swift index 0fbbab8b6c9..002e005b430 100644 --- a/Tests/BuildTests/BuildPlanTests.swift +++ b/Tests/BuildTests/BuildPlanTests.swift @@ -44,13 +44,13 @@ extension Build.BuildPlan { buildParameters: BuildParameters, graph: ModulesGraph, additionalFileRules: [FileRuleDescription] = [], - buildToolPluginInvocationResults: [ResolvedTarget.ID: [BuildToolPluginInvocationResult]] = [:], - prebuildCommandResults: [ResolvedTarget.ID: [PrebuildCommandResult]] = [:], + buildToolPluginInvocationResults: [ResolvedModule.ID: [BuildToolPluginInvocationResult]] = [:], + prebuildCommandResults: [ResolvedModule.ID: [PrebuildCommandResult]] = [:], fileSystem: any FileSystem, observabilityScope: ObservabilityScope ) throws { try self.init( - productsBuildParameters: buildParameters, + destinationBuildParameters: buildParameters, toolsBuildParameters: buildParameters, graph: graph, additionalFileRules: additionalFileRules, @@ -923,7 +923,7 @@ final class BuildPlanTests: XCTestCase { buildPath: buildDirPath, config: .release, toolchain: UserToolchain.default, - targetTriple: UserToolchain.default.targetTriple, + triple: UserToolchain.default.targetTriple, useExplicitModuleBuild: true ), graph: graph, @@ -1153,11 +1153,11 @@ final class BuildPlanTests: XCTestCase { observabilityScope: observability.topScope )) - XCTAssertEqual(Set(result.productMap.keys), ["APackageTests"]) + XCTAssertEqual(Set(result.productMap.keys.map(\.productName)), ["APackageTests"]) #if os(macOS) - XCTAssertEqual(Set(result.targetMap.keys), ["ATarget", "BTarget", "ATargetTests"]) + XCTAssertEqual(Set(result.targetMap.keys.map(\.targetName)), ["ATarget", "BTarget", "ATargetTests"]) #else - XCTAssertEqual(Set(result.targetMap.keys), [ + XCTAssertEqual(Set(result.targetMap.keys.map(\.targetName)), [ "APackageTests", "APackageDiscoveredTests", "ATarget", @@ -1507,7 +1507,13 @@ final class BuildPlanTests: XCTestCase { ]) #endif - let buildProduct = try XCTUnwrap(result.productMap["exe"]) + let buildProduct = try XCTUnwrap( + result.productMap[.init( + productName: "exe", + packageIdentity: "Pkg", + buildTriple: .destination + )] + ) XCTAssertEqual(Array(buildProduct.objects), [ buildPath.appending(components: "exe.build", "main.c.o"), buildPath.appending(components: "extlib.build", "extlib.c.o"), @@ -1751,8 +1757,9 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) + let buildParameters = mockBuildParameters() let plan = try BuildPlan( - buildParameters: mockBuildParameters(), + buildParameters: buildParameters, graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -1824,7 +1831,7 @@ final class BuildPlanTests: XCTestCase { "@\(buildPath.appending(components: "exe.product", "Objects.LinkFileList"))", "-Xlinker", "-rpath", "-Xlinker", "/fake/path/lib/swift-5.5/macosx", "-target", defaultTargetTriple, - "-Xlinker", "-add_ast_path", "-Xlinker", "/path/to/build/debug/exe.build/exe.swiftmodule", + "-Xlinker", "-add_ast_path", "-Xlinker", "/path/to/build/\(buildParameters.triple)/debug/exe.build/exe.swiftmodule", "-g", ]) #elseif os(Windows) @@ -1880,8 +1887,9 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) + let buildParameters = mockBuildParameters() let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(), + buildParameters: buildParameters, graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -1891,8 +1899,8 @@ final class BuildPlanTests: XCTestCase { let lib = try result.target(for: "lib").clangTarget() XCTAssertEqual(try lib.objects, [ - AbsolutePath("/path/to/build/debug/lib.build/lib.S.o"), - AbsolutePath("/path/to/build/debug/lib.build/lib.c.o"), + AbsolutePath("/path/to/build/\(buildParameters.triple)/debug/lib.build/lib.S.o"), + AbsolutePath("/path/to/build/\(buildParameters.triple)/debug/lib.build/lib.c.o"), ]) } @@ -2610,7 +2618,7 @@ final class BuildPlanTests: XCTestCase { // Verify that `-lstdc++` is passed instead of `-lc++` when cross-compiling to Linux. result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(targetTriple: .arm64Linux), + buildParameters: mockBuildParameters(triple: .arm64Linux), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -3421,7 +3429,7 @@ final class BuildPlanTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(targetTriple: .windows), + buildParameters: mockBuildParameters(triple: .windows), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -3505,7 +3513,7 @@ final class BuildPlanTests: XCTestCase { try BuildPlanResult(plan: BuildPlan( buildParameters: mockBuildParameters( canRenameEntrypointFunctionName: true, - targetTriple: triple + triple: triple ), graph: graph, fileSystem: fs, @@ -3702,7 +3710,7 @@ final class BuildPlanTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(targetTriple: .init("arm64-apple-ios")), + buildParameters: mockBuildParameters(triple: .init("arm64-apple-ios")), graph: graph, fileSystem: fileSystem, observabilityScope: observability.topScope @@ -3779,7 +3787,7 @@ final class BuildPlanTests: XCTestCase { // constraints above are valid. XCTAssertNoThrow( _ = try BuildPlan( - buildParameters: mockBuildParameters(targetTriple: .arm64iOS), + buildParameters: mockBuildParameters(triple: .arm64iOS), graph: graph, fileSystem: fileSystem, observabilityScope: observability.topScope @@ -3789,7 +3797,7 @@ final class BuildPlanTests: XCTestCase { // For completeness, the invalid target should still throw an error. XCTAssertThrows(Diagnostics.fatalError) { _ = try BuildPlan( - buildParameters: mockBuildParameters(targetTriple: .x86_64MacOS), + buildParameters: mockBuildParameters(triple: .x86_64MacOS), graph: graph, fileSystem: fileSystem, observabilityScope: observability.topScope @@ -3852,7 +3860,7 @@ final class BuildPlanTests: XCTestCase { XCTAssertThrows(Diagnostics.fatalError) { _ = try BuildPlan( - buildParameters: mockBuildParameters(targetTriple: .x86_64MacOS), + buildParameters: mockBuildParameters(triple: .x86_64MacOS), graph: graph, fileSystem: fileSystem, observabilityScope: observability.topScope @@ -4018,7 +4026,7 @@ final class BuildPlanTests: XCTestCase { func createResult(for dest: Basics.Triple) throws -> BuildPlanResult { try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(targetTriple: dest), + buildParameters: mockBuildParameters(triple: dest), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -4086,7 +4094,7 @@ final class BuildPlanTests: XCTestCase { do { let result = try BuildPlanResult(plan: BuildPlan( buildParameters: mockBuildParameters( - targetTriple: .x86_64Linux, + triple: .x86_64Linux, omitFramePointers: true ), graph: graph, @@ -4143,7 +4151,7 @@ final class BuildPlanTests: XCTestCase { do { let result = try BuildPlanResult(plan: BuildPlan( buildParameters: mockBuildParameters( - targetTriple: .x86_64Linux, + triple: .x86_64Linux, omitFramePointers: false ), graph: graph, @@ -4507,7 +4515,7 @@ final class BuildPlanTests: XCTestCase { swiftCompilerFlags: [cliFlag(tool: .swiftCompiler)], linkerFlags: [cliFlag(tool: .linker)] ), - targetTriple: targetTriple + triple: targetTriple ) let result = try BuildPlanResult(plan: BuildPlan( buildParameters: buildParameters, @@ -4792,8 +4800,9 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) + let buildParameters = mockBuildParameters() let plan = try BuildPlan( - buildParameters: mockBuildParameters(), + buildParameters: buildParameters, graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -4809,7 +4818,7 @@ final class BuildPlanTests: XCTestCase { [ .anySequence, "-emit-objc-header", - "-emit-objc-header-path", "/path/to/build/debug/Foo.build/Foo-Swift.h", + "-emit-objc-header-path", "/path/to/build/\(buildParameters.triple)/debug/Foo.build/Foo-Swift.h", .anySequence, ] ) @@ -4819,7 +4828,7 @@ final class BuildPlanTests: XCTestCase { [ .anySequence, "-emit-objc-header", - "-emit-objc-header-path", "/path/to/build/debug/Foo.build/Foo-Swift.h", + "-emit-objc-header-path", "/path/to/build/\(buildParameters.triple)/Foo.build/Foo-Swift.h", .anySequence, ] ) @@ -4829,12 +4838,12 @@ final class BuildPlanTests: XCTestCase { #if os(macOS) XCTAssertMatch( barTarget, - [.anySequence, "-fmodule-map-file=/path/to/build/debug/Foo.build/module.modulemap", .anySequence] + [.anySequence, "-fmodule-map-file=/path/to/build/\(buildParameters.triple)/debug/Foo.build/module.modulemap", .anySequence] ) #else XCTAssertNoMatch( barTarget, - [.anySequence, "-fmodule-map-file=/path/to/build/debug/Foo.build/module.modulemap", .anySequence] + [.anySequence, "-fmodule-map-file=/path/to/build/\(buildParameters.triple)/debug/Foo.build/module.modulemap", .anySequence] ) #endif @@ -4892,8 +4901,9 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) + let buildParameters = mockBuildParameters() let plan = try BuildPlan( - buildParameters: mockBuildParameters(), + buildParameters: buildParameters, graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -4910,7 +4920,7 @@ final class BuildPlanTests: XCTestCase { .anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/Foo.build/Foo-Swift.h", + "/path/to/build/\(buildParameters.triple)/debug/Foo.build/Foo-Swift.h", .anySequence, ] ) @@ -4921,7 +4931,7 @@ final class BuildPlanTests: XCTestCase { .anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/Foo.build/Foo-Swift.h", + "/path/to/build/\(buildParameters.triple)/debug/Foo.build/Foo-Swift.h", .anySequence, ] ) @@ -4933,7 +4943,7 @@ final class BuildPlanTests: XCTestCase { barTarget, [ .anySequence, - "-fmodule-map-file=/path/to/build/debug/Foo.build/module.modulemap", + "-fmodule-map-file=/path/to/build/\(buildParameters.triple)/debug/Foo.build/module.modulemap", .anySequence, ] ) @@ -4942,7 +4952,7 @@ final class BuildPlanTests: XCTestCase { barTarget, [ .anySequence, - "-fmodule-map-file=/path/to/build/debug/Foo.build/module.modulemap", + "-fmodule-map-file=/path/to/build/\(buildParameters.triple)/debug/Foo.build/module.modulemap", .anySequence, ] ) @@ -5002,8 +5012,9 @@ final class BuildPlanTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) + let buildParameters = mockBuildParameters() let plan = try BuildPlan( - buildParameters: mockBuildParameters(), + buildParameters: buildParameters, graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -5024,7 +5035,7 @@ final class BuildPlanTests: XCTestCase { .anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/Foo.build/Foo-Swift.h", + "/path/to/build/\(buildParameters.triple)/debug/Foo.build/Foo-Swift.h", .anySequence, ] ) @@ -5035,7 +5046,7 @@ final class BuildPlanTests: XCTestCase { .anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/Foo.build/Foo-Swift.h", + "/path/to/build/\(buildParameters.triple)/debug/Foo.build/Foo-Swift.h", .anySequence, ] ) @@ -5047,7 +5058,7 @@ final class BuildPlanTests: XCTestCase { barTarget, [ .anySequence, - "-fmodule-map-file=/path/to/build/debug/Foo.build/module.modulemap", + "-fmodule-map-file=/path/to/build/\(buildParameters.triple)/debug/Foo.build/module.modulemap", .anySequence, ] ) @@ -5056,7 +5067,7 @@ final class BuildPlanTests: XCTestCase { barTarget, [ .anySequence, - "-fmodule-map-file=/path/to/build/debug/Foo.build/module.modulemap", + "-fmodule-map-file=/path/to/build/\(buildParameters.triple)/debug/Foo.build/module.modulemap", .anySequence, ] ) @@ -5106,7 +5117,7 @@ final class BuildPlanTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(targetTriple: .x86_64Linux), + buildParameters: mockBuildParameters(triple: .x86_64Linux), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -5410,7 +5421,7 @@ final class BuildPlanTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) let plan = try BuildPlan( - buildParameters: mockBuildParameters(targetTriple: .wasi), + buildParameters: mockBuildParameters(triple: .wasi), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -5543,7 +5554,7 @@ final class BuildPlanTests: XCTestCase { let supportingTriples: [Basics.Triple] = [.x86_64Linux, .arm64Linux, .wasi] for triple in supportingTriples { let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true, targetTriple: triple), + buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true, triple: triple), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -5668,7 +5679,7 @@ final class BuildPlanTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(targetTriple: targetTriple), + buildParameters: mockBuildParameters(triple: targetTriple), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -5797,7 +5808,7 @@ final class BuildPlanTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) let result = try BuildPlanResult(plan: BuildPlan( - buildParameters: mockBuildParameters(targetTriple: targetTriple), + buildParameters: mockBuildParameters(triple: targetTriple), graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -6070,7 +6081,13 @@ final class BuildPlanTests: XCTestCase { observabilityScope: observability.topScope )) - switch try XCTUnwrap(result.targetMap["ExtLib"]) { + switch try XCTUnwrap( + result.targetMap[.init( + targetName: "ExtLib", + packageIdentity: "ExtPkg", + buildTriple: .destination + )] + ) { case .swift(let swiftTarget): if #available(macOS 13, *) { // `.contains` is only available in macOS 13 or newer XCTAssertTrue(try swiftTarget.compileArguments().contains(["-user-module-version", "1.0.0"])) @@ -6228,7 +6245,13 @@ final class BuildPlanTests: XCTestCase { result.checkTargetsCount(3) XCTAssertTrue(result.targetMap.values.contains { $0.target.name == "FooLogging" }) XCTAssertTrue(result.targetMap.values.contains { $0.target.name == "BarLogging" }) - let buildProduct = try XCTUnwrap(result.productMap["exe"]) + let buildProduct = try XCTUnwrap( + result.productMap[.init( + productName: "exe", + packageIdentity: "thisPkg", + buildTriple: .destination + )] + ) let dylibs = Array(buildProduct.dylibs.map({$0.product.name})).sorted() XCTAssertEqual(dylibs, ["BarLogging", "FooLogging"]) } diff --git a/Tests/BuildTests/ClangTargetBuildDescriptionTests.swift b/Tests/BuildTests/ClangTargetBuildDescriptionTests.swift index 6a80831300c..f8361e473df 100644 --- a/Tests/BuildTests/ClangTargetBuildDescriptionTests.swift +++ b/Tests/BuildTests/ClangTargetBuildDescriptionTests.swift @@ -28,9 +28,9 @@ final class ClangTargetBuildDescriptionTests: XCTestCase { func testSwiftCorelibsFoundationIncludeWorkaround() throws { let toolchain = MockToolchain(swiftResourcesPath: AbsolutePath("/fake/path/lib/swift")) - let macosParameters = mockBuildParameters(toolchain: toolchain, targetTriple: .macOS) - let linuxParameters = mockBuildParameters(toolchain: toolchain, targetTriple: .arm64Linux) - let androidParameters = mockBuildParameters(toolchain: toolchain, targetTriple: .arm64Android) + let macosParameters = mockBuildParameters(toolchain: toolchain, triple: .macOS) + let linuxParameters = mockBuildParameters(toolchain: toolchain, triple: .arm64Linux) + let androidParameters = mockBuildParameters(toolchain: toolchain, triple: .arm64Android) let macDescription = try makeTargetBuildDescription("swift-corelibs-foundation", buildParameters: macosParameters) @@ -65,8 +65,8 @@ final class ClangTargetBuildDescriptionTests: XCTestCase { ) } - private func makeResolvedTarget() throws -> ResolvedTarget { - ResolvedTarget( + private func makeResolvedTarget() throws -> ResolvedModule { + ResolvedModule( packageIdentity: .plain("dummy"), underlying: try makeClangTarget(), dependencies: [], diff --git a/Tests/BuildTests/CrossCompilationBuildPlanTests.swift b/Tests/BuildTests/CrossCompilationBuildPlanTests.swift index 65ac52d9076..4ac13002143 100644 --- a/Tests/BuildTests/CrossCompilationBuildPlanTests.swift +++ b/Tests/BuildTests/CrossCompilationBuildPlanTests.swift @@ -14,18 +14,18 @@ import struct Basics.AbsolutePath import class Basics.ObservabilitySystem import class Build.BuildPlan import class Build.ProductBuildDescription +import enum Build.TargetBuildDescription import class Build.SwiftTargetBuildDescription import struct Basics.Triple +import enum PackageGraph.BuildTriple import class PackageModel.Manifest import struct PackageModel.TargetDescription import func SPMTestSupport.loadPackageGraph import func SPMTestSupport.embeddedCxxInteropPackageGraph - import func SPMTestSupport.macrosPackageGraph - +import func SPMTestSupport.macrosTestsPackageGraph import func SPMTestSupport.mockBuildParameters - import func SPMTestSupport.trivialPackageGraph import struct SPMTestSupport.BuildPlanResult @@ -40,7 +40,7 @@ final class CrossCompilationBuildPlanTests: XCTestCase { var (graph, fs, observabilityScope) = try trivialPackageGraph(pkgRootPath: "/Pkg") let triple = try Triple("wasm32-unknown-none-wasm") - var parameters = mockBuildParameters(targetTriple: triple) + var parameters = mockBuildParameters(triple: triple) parameters.linkingParameters.shouldLinkStaticSwiftStdlib = true var result = try BuildPlanResult(plan: BuildPlan( buildParameters: parameters, @@ -103,7 +103,7 @@ final class CrossCompilationBuildPlanTests: XCTestCase { let (graph, fs, observabilityScope) = try trivialPackageGraph(pkgRootPath: pkgPath) var parameters = mockBuildParameters( - config: .release, targetTriple: .wasi, linkerDeadStrip: true + config: .release, triple: .wasi, linkerDeadStrip: true ) parameters.linkingParameters.shouldLinkStaticSwiftStdlib = true let result = try BuildPlanResult(plan: BuildPlan( @@ -135,7 +135,7 @@ final class CrossCompilationBuildPlanTests: XCTestCase { let (graph, fs, observabilityScope) = try trivialPackageGraph(pkgRootPath: pkgPath) - var parameters = mockBuildParameters(targetTriple: .wasi) + var parameters = mockBuildParameters(triple: .wasi) parameters.linkingParameters.shouldLinkStaticSwiftStdlib = true let result = try BuildPlanResult(plan: BuildPlan( buildParameters: parameters, @@ -150,7 +150,12 @@ final class CrossCompilationBuildPlanTests: XCTestCase { let buildPath = result.plan.productsBuildPath - let lib = try result.target(for: "lib").clangTarget() + let lib = try XCTUnwrap( + result.allTargets(named: "lib") + .map { try $0.clangTarget() } + .first { $0.target.buildTriple == .destination } + ) + XCTAssertEqual(try lib.basicArguments(isCXX: false), [ "-target", "wasm32-unknown-wasi", "-O0", "-DSWIFT_PACKAGE=1", "-DDEBUG=1", @@ -209,4 +214,130 @@ final class CrossCompilationBuildPlanTests: XCTestCase { let testPathExtension = try testBuildDescription.binaryPath.extension XCTAssertEqual(testPathExtension, "wasm") } + + func testMacros() throws { + let (graph, fs, scope) = try macrosPackageGraph() + + let destinationTriple = Triple.arm64Linux + let toolsTriple = Triple.x86_64MacOS + let plan = try BuildPlan( + destinationBuildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true, triple: destinationTriple), + toolsBuildParameters: mockBuildParameters(triple: toolsTriple), + graph: graph, + fileSystem: fs, + observabilityScope: scope + ) + let result = try BuildPlanResult(plan: plan) + result.checkProductsCount(3) + result.checkTargetsCount(10) + + XCTAssertTrue(try result.allTargets(named: "SwiftSyntax") + .map { try $0.swiftTarget() } + .contains { $0.target.buildTriple == .tools }) + try result.check(buildTriple: .tools, triple: toolsTriple, for: "MMIOMacros") + try result.check(buildTriple: .destination, triple: destinationTriple, for: "MMIO") + try result.check(buildTriple: .destination, triple: destinationTriple, for: "Core") + try result.check(buildTriple: .destination, triple: destinationTriple, for: "HAL") + + let macroProducts = result.allProducts(named: "MMIOMacros") + XCTAssertEqual(macroProducts.count, 1) + let macroProduct = try XCTUnwrap(macroProducts.first) + XCTAssertEqual(macroProduct.buildParameters.triple, toolsTriple) + + let mmioTargets = try result.allTargets(named: "MMIO").map { try $0.swiftTarget() } + XCTAssertEqual(mmioTargets.count, 1) + let mmioTarget = try XCTUnwrap(mmioTargets.first) + let compileArguments = try mmioTarget.emitCommandLine() + XCTAssertMatch( + compileArguments, + [ + "-I", .equal(mmioTarget.moduleOutputPath.parentDirectory.pathString), + .anySequence, + "-Xfrontend", "-load-plugin-executable", + // Verify that macros are located in the tools triple directory. + "-Xfrontend", .contains(toolsTriple.tripleString) + ] + ) + } + + func testMacrosTests() throws { + let (graph, fs, scope) = try macrosTestsPackageGraph() + + let destinationTriple = Triple.arm64Linux + let toolsTriple = Triple.x86_64MacOS + let plan = try BuildPlan( + destinationBuildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true, triple: destinationTriple), + toolsBuildParameters: mockBuildParameters(triple: toolsTriple), + graph: graph, + fileSystem: fs, + observabilityScope: scope + ) + let result = try BuildPlanResult(plan: plan) + result.checkProductsCount(2) + result.checkTargetsCount(15) + + XCTAssertTrue(try result.allTargets(named: "SwiftSyntax") + .map { try $0.swiftTarget() } + .contains { $0.target.buildTriple == .tools }) + + try result.check(buildTriple: .tools, triple: toolsTriple, for: "swift-mmioPackageTests") + try result.check(buildTriple: .tools, triple: toolsTriple, for: "swift-mmioPackageDiscoveredTests") + try result.check(buildTriple: .tools, triple: toolsTriple, for: "MMIOMacros") + try result.check(buildTriple: .destination, triple: destinationTriple, for: "MMIO") + try result.check(buildTriple: .tools, triple: toolsTriple, for: "MMIOMacrosTests") + + let macroProducts = result.allProducts(named: "MMIOMacros") + XCTAssertEqual(macroProducts.count, 1) + let macroProduct = try XCTUnwrap(macroProducts.first) + XCTAssertEqual(macroProduct.buildParameters.triple, toolsTriple) + + let mmioTargets = try result.allTargets(named: "MMIO").map { try $0.swiftTarget() } + XCTAssertEqual(mmioTargets.count, 1) + let mmioTarget = try XCTUnwrap(mmioTargets.first) + let compileArguments = try mmioTarget.emitCommandLine() + XCTAssertMatch( + compileArguments, + [ + "-I", .equal(mmioTarget.moduleOutputPath.parentDirectory.pathString), + .anySequence, + "-Xfrontend", "-load-plugin-executable", + // Verify that macros are located in the tools triple directory. + "-Xfrontend", .contains(toolsTriple.tripleString) + ] + ) + } +} + +extension BuildPlanResult { + func allTargets(named name: String) throws -> some Collection { + self.targetMap + .filter { $0.0.targetName == name } + .values + } + + func allProducts(named name: String) -> some Collection { + self.productMap + .filter { $0.0.productName == name } + .values + } + + func check( + buildTriple: BuildTriple, + triple: Triple, + for target: String, + file: StaticString = #file, + line: UInt = #line + ) throws { + let targets = self.targetMap.filter { + $0.key.targetName == target && $0.key.buildTriple == buildTriple + } + XCTAssertEqual(targets.count, 1, file: file, line: line) + + let target = try XCTUnwrap( + targets.first?.value, + file: file, + line: line + ).swiftTarget() + XCTAssertMatch(try target.emitCommandLine(), [.contains(triple.tripleString)], file: file, line: line) + } } diff --git a/Tests/BuildTests/LLBuildManifestBuilderTests.swift b/Tests/BuildTests/LLBuildManifestBuilderTests.swift index c9119b0a919..69fd9b181ee 100644 --- a/Tests/BuildTests/LLBuildManifestBuilderTests.swift +++ b/Tests/BuildTests/LLBuildManifestBuilderTests.swift @@ -52,7 +52,7 @@ final class LLBuildManifestBuilderTests: XCTestCase { configuration: .release )) var plan = try BuildPlan( - productsBuildParameters: buildParameters, + destinationBuildParameters: buildParameters, toolsBuildParameters: buildParameters, graph: graph, fileSystem: fs, @@ -69,8 +69,8 @@ final class LLBuildManifestBuilderTests: XCTestCase { ) try llbuild.createProductCommand(buildProduct) - let basicReleaseCommandNames = [ - AbsolutePath("/path/to/build/release/exe.product/Objects.LinkFileList").pathString, + var basicReleaseCommandNames = [ + AbsolutePath("/path/to/build/\(buildParameters.triple)/release/exe.product/Objects.LinkFileList").pathString, "", "C.exe-release.exe", ] @@ -87,7 +87,7 @@ final class LLBuildManifestBuilderTests: XCTestCase { configuration: .debug )) plan = try BuildPlan( - productsBuildParameters: buildParameters, + destinationBuildParameters: buildParameters, toolsBuildParameters: buildParameters, graph: graph, fileSystem: fs, @@ -97,12 +97,12 @@ final class LLBuildManifestBuilderTests: XCTestCase { result = try BuildPlanResult(plan: plan) buildProduct = try result.buildProduct(for: "exe") - llbuild = LLBuildManifestBuilder(plan, fileSystem: localFileSystem, observabilityScope: observability.topScope) + llbuild = LLBuildManifestBuilder(plan, fileSystem: fs, observabilityScope: observability.topScope) try llbuild.createProductCommand(buildProduct) let entitlementsCommandName = "C.exe-debug.exe-entitlements" - let basicDebugCommandNames = [ - AbsolutePath("/path/to/build/debug/exe.product/Objects.LinkFileList").pathString, + var basicDebugCommandNames = [ + AbsolutePath("/path/to/build/\(buildParameters.triple)/debug/exe.product/Objects.LinkFileList").pathString, "", "C.exe-debug.exe", ] @@ -110,7 +110,7 @@ final class LLBuildManifestBuilderTests: XCTestCase { XCTAssertEqual( llbuild.manifest.commands.map(\.key).sorted(), (basicDebugCommandNames + [ - AbsolutePath("/path/to/build/debug/exe-entitlement.plist").pathString, + AbsolutePath("/path/to/build/\(buildParameters.triple)/debug/exe-entitlement.plist").pathString, entitlementsCommandName, ]).sorted() ) @@ -123,8 +123,8 @@ final class LLBuildManifestBuilderTests: XCTestCase { XCTAssertEqual( entitlementsCommand.inputs, [ - .file("/path/to/build/debug/exe", isMutated: true), - .file("/path/to/build/debug/exe-entitlement.plist"), + .file("/path/to/build/\(buildParameters.triple)/debug/exe", isMutated: true), + .file("/path/to/build/\(buildParameters.triple)/debug/exe-entitlement.plist"), ] ) XCTAssertEqual( @@ -141,7 +141,7 @@ final class LLBuildManifestBuilderTests: XCTestCase { configuration: .release )) plan = try BuildPlan( - productsBuildParameters: buildParameters, + destinationBuildParameters: buildParameters, toolsBuildParameters: buildParameters, graph: graph, fileSystem: fs, @@ -154,6 +154,12 @@ final class LLBuildManifestBuilderTests: XCTestCase { llbuild = LLBuildManifestBuilder(plan, fileSystem: localFileSystem, observabilityScope: observability.topScope) try llbuild.createProductCommand(buildProduct) + basicReleaseCommandNames = [ + AbsolutePath("/path/to/build/\(buildParameters.triple)/release/exe.product/Objects.LinkFileList").pathString, + "", + "C.exe-release.exe", + ] + XCTAssertEqual( llbuild.manifest.commands.map(\.key).sorted(), basicReleaseCommandNames.sorted() @@ -166,7 +172,7 @@ final class LLBuildManifestBuilderTests: XCTestCase { configuration: .debug )) plan = try BuildPlan( - productsBuildParameters: buildParameters, + destinationBuildParameters: buildParameters, toolsBuildParameters: buildParameters, graph: graph, fileSystem: fs, @@ -176,12 +182,38 @@ final class LLBuildManifestBuilderTests: XCTestCase { result = try BuildPlanResult(plan: plan) buildProduct = try result.buildProduct(for: "exe") - llbuild = LLBuildManifestBuilder(plan, fileSystem: localFileSystem, observabilityScope: observability.topScope) + llbuild = LLBuildManifestBuilder(plan, fileSystem: fs, observabilityScope: observability.topScope) try llbuild.createProductCommand(buildProduct) + basicDebugCommandNames = [ + AbsolutePath("/path/to/build/\(buildParameters.triple)/debug/exe.product/Objects.LinkFileList").pathString, + "", + "C.exe-debug.exe", + ] + XCTAssertEqual( llbuild.manifest.commands.map(\.key).sorted(), basicDebugCommandNames.sorted() ) } + + /// Verifies that two targets with the same name but different triples don't share same build manifest keys. + func testToolsBuildTriple() throws { + let (graph, fs, scope) = try macrosPackageGraph() + let productsTriple = Triple.x86_64MacOS + let toolsTriple = Triple.arm64Linux + + let plan = try BuildPlan( + destinationBuildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true, triple: productsTriple), + toolsBuildParameters: mockBuildParameters(triple: toolsTriple), + graph: graph, + fileSystem: fs, + observabilityScope: scope + ) + + let builder = LLBuildManifestBuilder(plan, fileSystem: fs, observabilityScope: scope) + let manifest = try builder.generateManifest(at: "/manifest") + + XCTAssertNotNil(manifest.commands["C.SwiftSyntax-debug-tool.module"]) + } } diff --git a/Tests/BuildTests/ModuleAliasingBuildTests.swift b/Tests/BuildTests/ModuleAliasingBuildTests.swift index 23244d40a5a..6fb0b8d5d01 100644 --- a/Tests/BuildTests/ModuleAliasingBuildTests.swift +++ b/Tests/BuildTests/ModuleAliasingBuildTests.swift @@ -694,8 +694,9 @@ final class ModuleAliasingBuildTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) + let buildParameters = mockBuildParameters(shouldLinkStaticSwiftStdlib: true) let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + buildParameters: buildParameters, graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -724,33 +725,33 @@ final class ModuleAliasingBuildTests: XCTestCase { XCTAssertMatch( fooLoggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/FooLogging.build/FooLogging-Swift.h", .anySequence] + "/path/to/build/\(buildParameters.triple)/debug/FooLogging.build/FooLogging-Swift.h", .anySequence] ) XCTAssertMatch( barLoggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/BarLogging.build/BarLogging-Swift.h", .anySequence] + "/path/to/build/\(buildParameters.triple)/debug/BarLogging.build/BarLogging-Swift.h", .anySequence] ) XCTAssertMatch( loggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/Logging.build/Logging-Swift.h", .anySequence] + "/path/to/build/\(buildParameters.triple)/debug/Logging.build/Logging-Swift.h", .anySequence] ) #else XCTAssertNoMatch( fooLoggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/FooLogging.build/FooLogging-Swift.h", .anySequence] + "/path/to/build/\(buildParameters.triple)/debug/FooLogging.build/FooLogging-Swift.h", .anySequence] ) XCTAssertNoMatch( barLoggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/BarLogging.build/BarLogging-Swift.h", .anySequence] + "/path/to/build/\(buildParameters.triple)/debug/BarLogging.build/BarLogging-Swift.h", .anySequence] ) XCTAssertNoMatch( loggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/Logging.build/Logging-Swift.h", .anySequence] + "/path/to/build/\(buildParameters.triple)/debug/Logging.build/Logging-Swift.h", .anySequence] ) #endif } @@ -812,8 +813,9 @@ final class ModuleAliasingBuildTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) + let buildParameters = mockBuildParameters(shouldLinkStaticSwiftStdlib: true) let result = try BuildPlanResult(plan: try BuildPlan( - buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + buildParameters: buildParameters, graph: graph, fileSystem: fs, observabilityScope: observability.topScope @@ -842,23 +844,23 @@ final class ModuleAliasingBuildTests: XCTestCase { XCTAssertMatch( otherLoggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/OtherLogging.build/OtherLogging-Swift.h", .anySequence] + "/path/to/build/\(buildParameters.triple)/debug/OtherLogging.build/OtherLogging-Swift.h", .anySequence] ) XCTAssertMatch( loggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/Logging.build/Logging-Swift.h", .anySequence] + "/path/to/build/\(buildParameters.triple)/debug/Logging.build/Logging-Swift.h", .anySequence] ) #else XCTAssertNoMatch( otherLoggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/OtherLogging.build/OtherLogging-Swift.h", .anySequence] + "/path/to/build/\(buildParameters.triple)/debug/OtherLogging.build/OtherLogging-Swift.h", .anySequence] ) XCTAssertNoMatch( loggingArgs, [.anySequence, "-emit-objc-header", "-emit-objc-header-path", - "/path/to/build/debug/Logging.build/Logging-Swift.h", .anySequence] + "/path/to/build/\(buildParameters.triple)/debug/Logging.build/Logging-Swift.h", .anySequence] ) #endif } diff --git a/Tests/BuildTests/PluginsBuildPlanTests.swift b/Tests/BuildTests/PluginsBuildPlanTests.swift index 892c3da6725..7b2a1a78378 100644 --- a/Tests/BuildTests/PluginsBuildPlanTests.swift +++ b/Tests/BuildTests/PluginsBuildPlanTests.swift @@ -48,8 +48,16 @@ final class PluginsBuildPlanTests: XCTestCase { let (stdout, stderr) = try executeSwiftPackage(fixturePath, extraArgs: ["-v", "build-plugin-dependency"]) XCTAssertMatch(stdout, .contains("Hello from dependencies-stub")) XCTAssertMatch(stderr, .contains("Build of product 'plugintool' complete!")) - XCTAssertTrue(localFileSystem.exists(fixturePath.appending(RelativePath(".build/\(hostTriple)/debug/plugintool")))) - XCTAssertTrue(localFileSystem.exists(fixturePath.appending(RelativePath(".build/\(hostTriple)/debug/placeholder")))) + XCTAssertTrue( + localFileSystem.exists( + fixturePath.appending(RelativePath(".build/\(hostTriple)/debug/plugintool-tool")) + ) + ) + XCTAssertTrue( + localFileSystem.exists( + fixturePath.appending(RelativePath(".build/\(hostTriple)/debug/placeholder")) + ) + ) } // When cross compiling the final product, plugin dependencies should still be built for the host @@ -57,8 +65,16 @@ final class PluginsBuildPlanTests: XCTestCase { let (stdout, stderr) = try executeSwiftPackage(fixturePath, extraArgs: ["--triple", targetTriple, "-v", "build-plugin-dependency"]) XCTAssertMatch(stdout, .contains("Hello from dependencies-stub")) XCTAssertMatch(stderr, .contains("Build of product 'plugintool' complete!")) - XCTAssertTrue(localFileSystem.exists(fixturePath.appending(RelativePath(".build/\(hostTriple)/debug/plugintool")))) - XCTAssertTrue(localFileSystem.exists(fixturePath.appending(RelativePath(".build/\(targetTriple)/debug/placeholder")))) + XCTAssertTrue( + localFileSystem.exists( + fixturePath.appending(RelativePath(".build/\(hostTriple)/debug/plugintool-tool")) + ) + ) + XCTAssertTrue( + localFileSystem.exists( + fixturePath.appending(RelativePath(".build/\(targetTriple)/debug/placeholder")) + ) + ) } } } diff --git a/Tests/BuildTests/ProductBuildDescriptionTests.swift b/Tests/BuildTests/ProductBuildDescriptionTests.swift index f07646f64a5..444565cef7b 100644 --- a/Tests/BuildTests/ProductBuildDescriptionTests.swift +++ b/Tests/BuildTests/ProductBuildDescriptionTests.swift @@ -53,7 +53,7 @@ final class ProductBuildDescriptionTests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) - let id = ResolvedProduct.ID(targetName: "exe", packageIdentity: .plain("pkg"), buildTriple: .destination) + let id = ResolvedProduct.ID(productName: "exe", packageIdentity: .plain("pkg"), buildTriple: .destination) let package = try XCTUnwrap(graph.rootPackages.first) let product = try XCTUnwrap(graph.allProducts[id]) diff --git a/Tests/CommandsTests/PackageCommandTests.swift b/Tests/CommandsTests/PackageCommandTests.swift index 73e2d58db2d..b5a673a6aad 100644 --- a/Tests/CommandsTests/PackageCommandTests.swift +++ b/Tests/CommandsTests/PackageCommandTests.swift @@ -1719,9 +1719,15 @@ final class PackageCommandTests: CommandsTestCase { """ ) let hostTriple = try UserToolchain(swiftSDK: .hostSwiftSDK()).targetTriple - let hostTripleString = hostTriple.isDarwin() ? hostTriple.tripleString(forPlatformVersion: "") : hostTriple.tripleString - try localFileSystem.writeFileContents(packageDir.appending(components: "Binaries", "LocalBinaryTool.artifactbundle", "info.json"), string: - """ + let hostTripleString = if hostTriple.isDarwin() { + hostTriple.tripleString(forPlatformVersion: "") + } else { + hostTriple.tripleString + } + + try localFileSystem.writeFileContents( + packageDir.appending(components: "Binaries", "LocalBinaryTool.artifactbundle", "info.json"), + string: """ { "schemaVersion": "1.0", "artifacts": { "LocalBinaryTool": { @@ -1737,11 +1743,13 @@ final class PackageCommandTests: CommandsTestCase { } """ ) - try localFileSystem.writeFileContents(packageDir.appending(components: "Sources", "LocalBuiltTool", "main.swift"), string: - #"print("Hello")"# + try localFileSystem.writeFileContents( + packageDir.appending(components: "Sources", "LocalBuiltTool", "main.swift"), + string: #"print("Hello")"# ) - try localFileSystem.writeFileContents(packageDir.appending(components: "Plugins", "MyPlugin", "plugin.swift"), string: - """ + try localFileSystem.writeFileContents( + packageDir.appending(components: "Plugins", "MyPlugin", "plugin.swift"), + string: """ import PackagePlugin import Foundation @main @@ -1797,8 +1805,9 @@ final class PackageCommandTests: CommandsTestCase { ) // Create the sample vendored dependency package. - try localFileSystem.writeFileContents(packageDir.appending(components: "VendoredDependencies", "HelperPackage", "Package.swift"), string: - """ + try localFileSystem.writeFileContents( + packageDir.appending(components: "VendoredDependencies", "HelperPackage", "Package.swift"), + string: """ // swift-tools-version: 5.5 import PackageDescription let package = Package( @@ -1824,9 +1833,25 @@ final class PackageCommandTests: CommandsTestCase { ) """ ) - try localFileSystem.writeFileContents(packageDir.appending(components: "VendoredDependencies", "HelperPackage", "Sources", "HelperLibrary", "library.swift"), string: "public func Bar() { }" + try localFileSystem.writeFileContents( + packageDir.appending( + components: "VendoredDependencies", + "HelperPackage", + "Sources", + "HelperLibrary", + "library.swift" + ), + string: "public func Bar() { }" ) - try localFileSystem.writeFileContents(packageDir.appending(components: "VendoredDependencies", "HelperPackage", "Sources", "RemoteBuiltTool", "main.swift"), string: #"print("Hello")"# + try localFileSystem.writeFileContents( + packageDir.appending( + components: "VendoredDependencies", + "HelperPackage", + "Sources", + "RemoteBuiltTool", + "main.swift" + ), + string: #"print("Hello")"# ) // Check that we can invoke the plugin with the "plugin" subcommand. diff --git a/Tests/CommandsTests/SwiftCommandStateTests.swift b/Tests/CommandsTests/SwiftCommandStateTests.swift index b1d0b8fdfcc..071ee308448 100644 --- a/Tests/CommandsTests/SwiftCommandStateTests.swift +++ b/Tests/CommandsTests/SwiftCommandStateTests.swift @@ -258,7 +258,7 @@ final class SwiftCommandStateTests: CommandsTestCase { let explicitDwarfOptions = try GlobalOptions.parse(["--triple", "x86_64-unknown-windows-msvc", "-debug-info-format", "dwarf"]) let explicitDwarf = try SwiftCommandState.makeMockState(options: explicitDwarfOptions) plan = try BuildPlan( - productsBuildParameters: explicitDwarf.productsBuildParameters, + destinationBuildParameters: explicitDwarf.productsBuildParameters, toolsBuildParameters: explicitDwarf.toolsBuildParameters, graph: graph, fileSystem: fs, @@ -273,7 +273,7 @@ final class SwiftCommandStateTests: CommandsTestCase { let explicitCodeView = try SwiftCommandState.makeMockState(options: explicitCodeViewOptions) plan = try BuildPlan( - productsBuildParameters: explicitCodeView.productsBuildParameters, + destinationBuildParameters: explicitCodeView.productsBuildParameters, toolsBuildParameters: explicitCodeView.productsBuildParameters, graph: graph, fileSystem: fs, @@ -296,7 +296,7 @@ final class SwiftCommandStateTests: CommandsTestCase { let implicitDwarfOptions = try GlobalOptions.parse(["--triple", "x86_64-unknown-windows-msvc"]) let implicitDwarf = try SwiftCommandState.makeMockState(options: implicitDwarfOptions) plan = try BuildPlan( - productsBuildParameters: implicitDwarf.productsBuildParameters, + destinationBuildParameters: implicitDwarf.productsBuildParameters, toolsBuildParameters: implicitDwarf.toolsBuildParameters, graph: graph, fileSystem: fs, @@ -309,7 +309,7 @@ final class SwiftCommandStateTests: CommandsTestCase { let explicitNoDebugInfoOptions = try GlobalOptions.parse(["--triple", "x86_64-unknown-windows-msvc", "-debug-info-format", "none"]) let explicitNoDebugInfo = try SwiftCommandState.makeMockState(options: explicitNoDebugInfoOptions) plan = try BuildPlan( - productsBuildParameters: explicitNoDebugInfo.productsBuildParameters, + destinationBuildParameters: explicitNoDebugInfo.productsBuildParameters, toolsBuildParameters: explicitNoDebugInfo.toolsBuildParameters, graph: graph, fileSystem: fs, diff --git a/Tests/PackageGraphPerformanceTests/PackageGraphPerfTests.swift b/Tests/PackageGraphPerformanceTests/PackageGraphPerfTests.swift index c688b1d5439..6073cc22896 100644 --- a/Tests/PackageGraphPerformanceTests/PackageGraphPerfTests.swift +++ b/Tests/PackageGraphPerformanceTests/PackageGraphPerfTests.swift @@ -164,9 +164,9 @@ final class PackageGraphPerfTests: XCTestCasePerf { } func testRecursiveDependencies() throws { - var resolvedTarget = ResolvedTarget.mock(packageIdentity: "pkg", name: "t0") + var resolvedTarget = ResolvedModule.mock(packageIdentity: "pkg", name: "t0") for i in 1..<1000 { - resolvedTarget = ResolvedTarget.mock(packageIdentity: "pkg", name: "t\(i)", deps: resolvedTarget) + resolvedTarget = ResolvedModule.mock(packageIdentity: "pkg", name: "t\(i)", deps: resolvedTarget) } let N = 10 diff --git a/Tests/PackageGraphTests/CrossCompilationPackageGraphTests.swift b/Tests/PackageGraphTests/CrossCompilationPackageGraphTests.swift new file mode 100644 index 00000000000..dc50bab88c7 --- /dev/null +++ b/Tests/PackageGraphTests/CrossCompilationPackageGraphTests.swift @@ -0,0 +1,156 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +@testable +import SPMTestSupport + +@testable +import PackageGraph + +import XCTest + +final class CrossCompilationPackageGraphTests: XCTestCase { + func testTrivialPackage() throws { + let graph = try trivialPackageGraph(pkgRootPath: "/Pkg").graph + try PackageGraphTester(graph) { result in + result.check(packages: "Pkg") + // "SwiftSyntax" is included for both host and target triples and is not pruned on this level + result.check(targets: "app", "lib") + result.check(testModules: "test") + result.checkTarget("app") { result in + result.check(buildTriple: .destination) + result.check(dependencies: "lib") + } + try result.checkTargets("lib") { results in + let result = try XCTUnwrap(results.first { $0.target.buildTriple == .destination }) + result.check(dependencies: []) + } + result.checkTarget("test") { result in + result.check(buildTriple: .destination) + result.check(dependencies: "lib") + } + } + } + + func testMacros() throws { + let graph = try macrosPackageGraph().graph + try PackageGraphTester(graph) { result in + result.check(packages: "swift-firmware", "swift-mmio", "swift-syntax") + // "SwiftSyntax" is included for both host and target triples and is not pruned on this level + result.check( + targets: "Core", + "HAL", + "MMIO", + "MMIOMacros", + "SwiftSyntax", + "SwiftSyntax" + ) + result.check(testModules: "CoreTests", "HALTests") + try result.checkTargets("Core") { results in + let result = try XCTUnwrap(results.first { $0.target.buildTriple == .destination }) + result.check(dependencies: "HAL") + } + try result.checkTargets("HAL") { results in + let result = try XCTUnwrap(results.first { $0.target.buildTriple == .destination }) + result.check(buildTriple: .destination) + result.check(dependencies: "MMIO") + } + try result.checkTargets("MMIO") { results in + let result = try XCTUnwrap(results.first { $0.target.buildTriple == .destination }) + result.check(buildTriple: .destination) + result.check(dependencies: "MMIOMacros") + } + try result.checkTargets("MMIOMacros") { results in + let result = try XCTUnwrap(results.first(where: { $0.target.buildTriple == .tools })) + result.check(buildTriple: .tools) + result.checkDependency("SwiftSyntax") { result in + result.checkProduct { result in + result.check(buildTriple: .tools) + result.checkTarget("SwiftSyntax") { result in + result.check(buildTriple: .tools) + } + } + } + } + + result.checkTargets("SwiftSyntax") { results in + XCTAssertEqual(results.count, 2) + + XCTAssertEqual(results.filter({ $0.target.buildTriple == .tools }).count, 1) + XCTAssertEqual(results.filter({ $0.target.buildTriple == .destination }).count, 1) + } + } + } + + func testMacrosTests() throws { + let graph = try macrosTestsPackageGraph().graph + PackageGraphTester(graph) { result in + result.check(packages: "swift-mmio", "swift-syntax") + // "SwiftSyntax" is included for both host and target triples and is not pruned on this level + result.check( + targets: "MMIO", + "MMIOMacros", + "SwiftCompilerPlugin", + "SwiftCompilerPlugin", + "SwiftCompilerPluginMessageHandling", + "SwiftCompilerPluginMessageHandling", + "SwiftSyntax", + "SwiftSyntax", + "SwiftSyntaxMacros", + "SwiftSyntaxMacros", + "SwiftSyntaxMacrosTestSupport", + "SwiftSyntaxMacrosTestSupport" + ) + result.check(testModules: "MMIOMacrosTests") + result.checkTarget("MMIO") { result in + result.check(buildTriple: .destination) + result.check(dependencies: "MMIOMacros") + } + result.checkTargets("MMIOMacros") { results in + XCTAssertEqual(results.count, 1) + } + result.checkTarget("MMIOMacrosTests") { result in + result.check(buildTriple: .tools) + result.checkDependency("MMIOMacros") { result in + result.checkTarget { result in + result.check(buildTriple: .tools) + result.checkDependency("SwiftSyntaxMacros") { result in + result.checkProduct { result in + result.check(buildTriple: .tools) + } + } + result.checkDependency("SwiftCompilerPlugin") { result in + result.checkProduct { result in + result.check(buildTriple: .tools) + result.checkTarget("SwiftCompilerPlugin") { result in + result.check(buildTriple: .tools) + result.checkDependency("SwiftCompilerPluginMessageHandling") { result in + result.checkTarget { result in + result.check(buildTriple: .tools) + } + } + } + } + } + } + } + } + + result.checkTargets("SwiftSyntax") { results in + XCTAssertEqual(results.count, 2) + + XCTAssertEqual(results.filter({ $0.target.buildTriple == .tools }).count, 1) + XCTAssertEqual(results.filter({ $0.target.buildTriple == .destination }).count, 1) + } + } + } +} diff --git a/Tests/PackageGraphTests/ResolvedTargetTests.swift b/Tests/PackageGraphTests/ResolvedTargetTests.swift index 5c10466603e..5546a9cc267 100644 --- a/Tests/PackageGraphTests/ResolvedTargetTests.swift +++ b/Tests/PackageGraphTests/ResolvedTargetTests.swift @@ -17,29 +17,29 @@ import PackageGraph import SPMTestSupport private func XCTAssertEqualTargetIDs( - _ lhs: [ResolvedTarget], - _ rhs: [ResolvedTarget], + _ lhs: [ResolvedModule], + _ rhs: [ResolvedModule], file: StaticString = #filePath, line: UInt = #line ) { XCTAssertEqual(lhs.map(\.id), rhs.map(\.id), file: file, line: line) } -final class ResolvedTargetDependencyTests: XCTestCase { +final class ResolvedModuleDependencyTests: XCTestCase { func test1() throws { - let t1 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t1") - let t2 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t2", deps: t1) - let t3 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t3", deps: t2) + let t1 = ResolvedModule.mock(packageIdentity: "pkg", name: "t1") + let t2 = ResolvedModule.mock(packageIdentity: "pkg", name: "t2", deps: t1) + let t3 = ResolvedModule.mock(packageIdentity: "pkg", name: "t3", deps: t2) XCTAssertEqualTargetIDs(try t3.recursiveTargetDependencies(), [t2, t1]) XCTAssertEqualTargetIDs(try t2.recursiveTargetDependencies(), [t1]) } func test2() throws { - let t1 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t1") - let t2 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t2", deps: t1) - let t3 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t3", deps: t2, t1) - let t4 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t4", deps: t2, t3, t1) + let t1 = ResolvedModule.mock(packageIdentity: "pkg", name: "t1") + let t2 = ResolvedModule.mock(packageIdentity: "pkg", name: "t2", deps: t1) + let t3 = ResolvedModule.mock(packageIdentity: "pkg", name: "t3", deps: t2, t1) + let t4 = ResolvedModule.mock(packageIdentity: "pkg", name: "t4", deps: t2, t3, t1) XCTAssertEqualTargetIDs(try t4.recursiveTargetDependencies(), [t3, t2, t1]) XCTAssertEqualTargetIDs(try t3.recursiveTargetDependencies(), [t2, t1]) @@ -47,10 +47,10 @@ final class ResolvedTargetDependencyTests: XCTestCase { } func test3() throws { - let t1 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t1") - let t2 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t2", deps: t1) - let t3 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t3", deps: t2, t1) - let t4 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t4", deps: t1, t2, t3) + let t1 = ResolvedModule.mock(packageIdentity: "pkg", name: "t1") + let t2 = ResolvedModule.mock(packageIdentity: "pkg", name: "t2", deps: t1) + let t3 = ResolvedModule.mock(packageIdentity: "pkg", name: "t3", deps: t2, t1) + let t4 = ResolvedModule.mock(packageIdentity: "pkg", name: "t4", deps: t1, t2, t3) XCTAssertEqualTargetIDs(try t4.recursiveTargetDependencies(), [t3, t2, t1]) XCTAssertEqualTargetIDs(try t3.recursiveTargetDependencies(), [t2, t1]) @@ -58,10 +58,10 @@ final class ResolvedTargetDependencyTests: XCTestCase { } func test4() throws { - let t1 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t1") - let t2 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t2", deps: t1) - let t3 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t3", deps: t2) - let t4 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t4", deps: t3) + let t1 = ResolvedModule.mock(packageIdentity: "pkg", name: "t1") + let t2 = ResolvedModule.mock(packageIdentity: "pkg", name: "t2", deps: t1) + let t3 = ResolvedModule.mock(packageIdentity: "pkg", name: "t3", deps: t2) + let t4 = ResolvedModule.mock(packageIdentity: "pkg", name: "t4", deps: t3) XCTAssertEqualTargetIDs(try t4.recursiveTargetDependencies(), [t3, t2, t1]) XCTAssertEqualTargetIDs(try t3.recursiveTargetDependencies(), [t2, t1]) @@ -69,12 +69,12 @@ final class ResolvedTargetDependencyTests: XCTestCase { } func test5() throws { - let t1 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t1") - let t2 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t2", deps: t1) - let t3 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t3", deps: t2) - let t4 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t4", deps: t3) - let t5 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t5", deps: t2) - let t6 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t6", deps: t5, t4) + let t1 = ResolvedModule.mock(packageIdentity: "pkg", name: "t1") + let t2 = ResolvedModule.mock(packageIdentity: "pkg", name: "t2", deps: t1) + let t3 = ResolvedModule.mock(packageIdentity: "pkg", name: "t3", deps: t2) + let t4 = ResolvedModule.mock(packageIdentity: "pkg", name: "t4", deps: t3) + let t5 = ResolvedModule.mock(packageIdentity: "pkg", name: "t5", deps: t2) + let t6 = ResolvedModule.mock(packageIdentity: "pkg", name: "t6", deps: t5, t4) // precise order is not important, but it is important that the following are true let t6rd = try t6.recursiveTargetDependencies().map(\.id) @@ -91,12 +91,12 @@ final class ResolvedTargetDependencyTests: XCTestCase { } func test6() throws { - let t1 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t1") - let t2 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t2", deps: t1) - let t3 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t3", deps: t2) - let t4 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t4", deps: t3) - let t5 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t5", deps: t2) - let t6 = ResolvedTarget.mock( + let t1 = ResolvedModule.mock(packageIdentity: "pkg", name: "t1") + let t2 = ResolvedModule.mock(packageIdentity: "pkg", name: "t2", deps: t1) + let t3 = ResolvedModule.mock(packageIdentity: "pkg", name: "t3", deps: t2) + let t4 = ResolvedModule.mock(packageIdentity: "pkg", name: "t4", deps: t3) + let t5 = ResolvedModule.mock(packageIdentity: "pkg", name: "t5", deps: t2) + let t6 = ResolvedModule.mock( packageIdentity: "pkg", name: "t6", deps: t4, @@ -118,10 +118,10 @@ final class ResolvedTargetDependencyTests: XCTestCase { } func testConditions() throws { - let t1 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t1") - let t2 = ResolvedTarget.mock(packageIdentity: "pkg", name: "t2", deps: t1) - let t2NoConditions = ResolvedTarget.mock(packageIdentity: "pkg", name: "t2", deps: t1) - let t2WithConditions = ResolvedTarget.mock( + let t1 = ResolvedModule.mock(packageIdentity: "pkg", name: "t1") + let t2 = ResolvedModule.mock(packageIdentity: "pkg", name: "t2", deps: t1) + let t2NoConditions = ResolvedModule.mock(packageIdentity: "pkg", name: "t2", deps: t1) + let t2WithConditions = ResolvedModule.mock( packageIdentity: "pkg", name: "t2", deps: t1, diff --git a/Tests/SPMBuildCoreTests/PluginInvocationTests.swift b/Tests/SPMBuildCoreTests/PluginInvocationTests.swift index d06d5bb133d..6f35776a211 100644 --- a/Tests/SPMBuildCoreTests/PluginInvocationTests.swift +++ b/Tests/SPMBuildCoreTests/PluginInvocationTests.swift @@ -34,7 +34,7 @@ class PluginInvocationTests: XCTestCase { "/Foo/Sources/Foo/SomeFile.abc" ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fileSystem, manifests: [ Manifest.createRootManifest( @@ -74,7 +74,8 @@ class PluginInvocationTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) PackageGraphTester(graph) { graph in graph.check(packages: "Foo") - graph.check(targets: "Foo", "FooPlugin", "FooTool") + // "FooTool" duplicated as it's present for both build tools and end products triples. + graph.check(targets: "Foo", "FooPlugin", "FooTool", "FooTool") graph.checkTarget("Foo") { target in target.check(dependencies: "FooPlugin") } @@ -82,8 +83,10 @@ class PluginInvocationTests: XCTestCase { target.check(type: .plugin) target.check(dependencies: "FooTool") } - graph.checkTarget("FooTool") { target in - target.check(type: .executable) + graph.checkTargets("FooTool") { targets in + for target in targets { + target.check(type: .executable) + } } } @@ -188,13 +191,13 @@ class PluginInvocationTests: XCTestCase { // Construct a canned input and run plugins using our MockPluginScriptRunner(). let outputDir = AbsolutePath("/Foo/.build") - let builtToolsDir = AbsolutePath("/path/to/build/debug") let pluginRunner = MockPluginScriptRunner() + let buildParameters = mockBuildParameters( + environment: BuildEnvironment(platform: .macOS, configuration: .debug) + ) let results = try graph.invokeBuildToolPlugins( outputDir: outputDir, - buildParameters: mockBuildParameters( - environment: BuildEnvironment(platform: .macOS, configuration: .debug) - ), + buildParameters: buildParameters, additionalFileRules: [], toolSearchDirectories: [UserToolchain.default.swiftCompilerPath.parentDirectory], pkgConfigDirectories: [], @@ -202,11 +205,12 @@ class PluginInvocationTests: XCTestCase { observabilityScope: observability.topScope, fileSystem: fileSystem ) + let builtToolsDir = AbsolutePath("/path/to/build/\(buildParameters.triple)/debug") // Check the canned output to make sure nothing was lost in transport. XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertEqual(results.count, 1) - let (evalTargetID, (evalTarget, evalResults)) = try XCTUnwrap(results.first) + let (_, (evalTarget, evalResults)) = try XCTUnwrap(results.first) XCTAssertEqual(evalTarget.name, "Foo") XCTAssertEqual(evalResults.count, 1) @@ -1081,7 +1085,6 @@ class PluginInvocationTests: XCTestCase { } } - XCTAssertEqual(count, 2) } } @@ -1089,7 +1092,7 @@ class PluginInvocationTests: XCTestCase { func checkParseArtifactsPlatformCompatibility( artifactSupportedTriples: [Triple], hostTriple: Triple - ) async throws -> [ResolvedTarget.ID: [BuildToolPluginInvocationResult]] { + ) async throws -> [ResolvedModule.ID: [BuildToolPluginInvocationResult]] { // Only run the test if the environment in which we're running actually supports Swift concurrency (which the plugin APIs require). try XCTSkipIf(!UserToolchain.default.supportsSwiftConcurrency(), "skipping because test environment doesn't support concurrency") diff --git a/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift b/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift index 127710d0785..759106a8fa6 100644 --- a/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift +++ b/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift @@ -27,7 +27,7 @@ class SourceKitLSPAPITests: XCTestCase { ) let observability = ObservabilitySystem.makeForTesting() - let graph = try loadPackageGraph( + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( @@ -42,22 +42,45 @@ class SourceKitLSPAPITests: XCTestCase { ) XCTAssertNoDiagnostics(observability.diagnostics) + let buildParameters = mockBuildParameters(shouldLinkStaticSwiftStdlib: true) let plan = try BuildPlan( - productsBuildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), - toolsBuildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), + destinationBuildParameters: buildParameters, + toolsBuildParameters: buildParameters, graph: graph, fileSystem: fs, observabilityScope: observability.topScope ) let description = BuildDescription(buildPlan: plan) - try description.checkArguments(for: "exe", graph: graph, partialArguments: ["-module-name", "exe", "-emit-dependencies", "-emit-module", "-emit-module-path", "/path/to/build/debug/exe.build/exe.swiftmodule"]) - try description.checkArguments(for: "lib", graph: graph, partialArguments: ["-module-name", "lib", "-emit-dependencies", "-emit-module", "-emit-module-path", "/path/to/build/debug/Modules/lib.swiftmodule"]) + try description.checkArguments( + for: "exe", + graph: graph, + partialArguments: [ + "-module-name", "exe", + "-emit-dependencies", + "-emit-module", + "-emit-module-path", "/path/to/build/\(buildParameters.triple)/debug/exe.build/exe.swiftmodule" + ] + ) + try description.checkArguments( + for: "lib", + graph: graph, + partialArguments: [ + "-module-name", "lib", + "-emit-dependencies", + "-emit-module", + "-emit-module-path", "/path/to/build/\(buildParameters.triple)/debug/Modules/lib.swiftmodule" + ] + ) } } extension SourceKitLSPAPI.BuildDescription { - @discardableResult func checkArguments(for targetName: String, graph: ModulesGraph, partialArguments: [String]) throws -> Bool { + @discardableResult func checkArguments( + for targetName: String, + graph: ModulesGraph, + partialArguments: [String] + ) throws -> Bool { let target = try XCTUnwrap(graph.allTargets.first(where: { $0.name == targetName })) let buildTarget = try XCTUnwrap(self.getBuildTarget(for: target)) From 9672bba770b28a76040972b60ad25e7ffabba888 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 18 Apr 2024 03:44:54 +0100 Subject: [PATCH 091/159] Add simple synthetic modules graph benchmarks (#7465) New `SyntheticModulesGraph` benchmark is introduced, which calls `loadModulesGraph` function that we already run against test fixtures in `ModulesGraphTests` and `BuildPlanTests`. The function under benchmark almost immediately delegates to `ModulesGraph.load`, which is used in real-world modules graph dependency resolution. Benchmark parameters are controlled with `SWIFTPM_BENCHMARK_MODULES_GRAPH_DEPTH` and `SWIFTPM_BENCHMARK_MODULES_GRAPH_WIDTH` environment variables, so in the future we should be able to plot graphs of benchmark metrics against depth and width of modules graph over a given range. Thresholds are now split into platform-specific directories so that thresholds recorded on x86_64 don't interfere with thresholds for arm64, same for the OS family. --- .../PackageGraphBenchmarks.swift | 83 ++++++++++++++++++- Benchmarks/README.md | 17 ++-- ...aphBenchmarks.PackageGraphLoading.p90.json | 4 - ...arks.SwiftPMWorkspaceModulesGraph.p90.json | 4 + ...hBenchmarks.SyntheticModulesGraph.p90.json | 4 + .../FileSystem/FileSystem+Extensions.swift | 46 ++++++++++ Sources/Basics/Graph/DirectedGraph.swift | 12 +-- Sources/Basics/Graph/UndirectedGraph.swift | 9 +- Sources/Basics/Observability.swift | 4 + Sources/PackageGraph/ModulesGraph.swift | 49 +++++++++++ .../SPMTestSupport/MockPackageGraphs.swift | 5 ++ Sources/SPMTestSupport/Observability.swift | 4 - Sources/SPMTestSupport/misc.swift | 49 +---------- .../Graph/DirectedGraphTests.swift | 4 +- .../Graph/UndirectedGraphTests.swift | 4 +- Tests/BuildTests/BuildPlanTests.swift | 2 + .../LLBuildManifestBuilderTests.swift | 3 + .../BuildTests/ModuleAliasingBuildTests.swift | 3 + .../ProductBuildDescriptionTests.swift | 4 +- .../MermaidPackageSerializerTests.swift | 5 +- Tests/CommandsTests/PackageCommandTests.swift | 3 + .../SwiftCommandStateTests.swift | 4 + .../PackageGraphPerfTests.swift | 3 + .../PackageGraphTests/ModulesGraphTests.swift | 3 + .../XCBuildSupportTests/PIFBuilderTests.swift | 3 + 25 files changed, 257 insertions(+), 74 deletions(-) delete mode 100644 Benchmarks/Thresholds/PackageGraphBenchmarks.PackageGraphLoading.p90.json create mode 100644 Benchmarks/Thresholds/macosx-arm64/PackageGraphBenchmarks.SwiftPMWorkspaceModulesGraph.p90.json create mode 100644 Benchmarks/Thresholds/macosx-arm64/PackageGraphBenchmarks.SyntheticModulesGraph.p90.json diff --git a/Benchmarks/Benchmarks/PackageGraphBenchmarks/PackageGraphBenchmarks.swift b/Benchmarks/Benchmarks/PackageGraphBenchmarks/PackageGraphBenchmarks.swift index 4846f30d80a..1c4d38061e4 100644 --- a/Benchmarks/Benchmarks/PackageGraphBenchmarks/PackageGraphBenchmarks.swift +++ b/Benchmarks/Benchmarks/PackageGraphBenchmarks/PackageGraphBenchmarks.swift @@ -1,7 +1,13 @@ +@_spi(DontAdoptOutsideOfSwiftPMExposedForBenchmarksAndTestsOnly) import Basics import Benchmark import Foundation import PackageModel + +@_spi(DontAdoptOutsideOfSwiftPMExposedForBenchmarksAndTestsOnly) +import func PackageGraph.loadModulesGraph + +import class TSCBasic.InMemoryFileSystem import Workspace let benchmarks = { @@ -16,6 +22,32 @@ let benchmarks = { ] } + let modulesGraphDepth: Int + if let envVar = ProcessInfo.processInfo.environment["SWIFTPM_BENCHMARK_MODULES_GRAPH_DEPTH"], + let parsedValue = Int(envVar) { + modulesGraphDepth = parsedValue + } else { + modulesGraphDepth = 100 + } + + let modulesGraphWidth: Int + if let envVar = ProcessInfo.processInfo.environment["SWIFTPM_BENCHMARK_MODULES_GRAPH_WIDTH"], + let parsedValue = Int(envVar) { + modulesGraphWidth = parsedValue + } else { + modulesGraphWidth = 100 + } + + let packagesGraphDepth: Int + if let envVar = ProcessInfo.processInfo.environment["SWIFTPM_BENCHMARK_PACKAGES_GRAPH_DEPTH"], + let parsedValue = Int(envVar) { + packagesGraphDepth = parsedValue + } else { + packagesGraphDepth = 10 + } + + let noopObservability = ObservabilitySystem.NOOP + // Benchmarks computation of a resolved graph of modules for a package using `Workspace` as an entry point. It runs PubGrub to get // resolved concrete versions of dependencies, assigning all modules and products to each other as corresponding dependencies // with their build triples, but with the build plan not yet constructed. In this benchmark specifically we're loading `Package.swift` @@ -33,10 +65,57 @@ let benchmarks = { ) { benchmark in let path = try AbsolutePath(validating: #file).parentDirectory.parentDirectory.parentDirectory let workspace = try Workspace(fileSystem: localFileSystem, location: .init(forRootPackage: path, fileSystem: localFileSystem)) - let system = ObservabilitySystem { _, _ in } for _ in benchmark.scaledIterations { - try workspace.loadPackageGraph(rootPath: path, observabilityScope: system.topScope) + try workspace.loadPackageGraph(rootPath: path, observabilityScope: noopObservability) + } + } + + + // Benchmarks computation of a resolved graph of modules for a synthesized package using `loadModulesGraph` as an + // entry point, which almost immediately delegates to `ModulesGraph.load` under the hood. + Benchmark( + "SyntheticModulesGraph", + configuration: .init( + metrics: defaultMetrics, + maxDuration: .seconds(10), + thresholds: [ + .mallocCountTotal: .init(absolute: [.p90: 2500]), + .syscalls: .init(absolute: [.p90: 0]), + ] + ) + ) { benchmark in + let targets = try (0.. { - init(nodes: [Node]) { +@_spi(DontAdoptOutsideOfSwiftPMExposedForBenchmarksAndTestsOnly) +public struct DirectedGraph { + public init(nodes: [Node]) { self.nodes = nodes self.edges = .init(repeating: [], count: nodes.count) } - private var nodes: [Node] + public private(set) var nodes: [Node] private var edges: [[Int]] - mutating func addEdge(source: Int, destination: Int) { + public mutating func addEdge(source: Int, destination: Int) { self.edges[source].append(destination) } @@ -31,7 +32,8 @@ struct DirectedGraph { /// - source: `Index` of a node to start traversing edges from. /// - destination: `Index` of a node to which a path could exist via edges from `source`. /// - Returns: `true` if a path from `source` to `destination` exists, `false` otherwise. - func areNodesConnected(source: Int, destination: Int) -> Bool { + @_spi(DontAdoptOutsideOfSwiftPMExposedForBenchmarksAndTestsOnly) + public func areNodesConnected(source: Int, destination: Int) -> Bool { var todo = Deque([source]) var done = Set() diff --git a/Sources/Basics/Graph/UndirectedGraph.swift b/Sources/Basics/Graph/UndirectedGraph.swift index 02a0bdca90c..87fda92d854 100644 --- a/Sources/Basics/Graph/UndirectedGraph.swift +++ b/Sources/Basics/Graph/UndirectedGraph.swift @@ -13,8 +13,9 @@ import struct DequeModule.Deque /// Undirected graph that stores edges in an [adjacency matrix](https://en.wikipedia.org/wiki/Adjacency_list). -struct UndirectedGraph { - init(nodes: [Node]) { +@_spi(DontAdoptOutsideOfSwiftPMExposedForBenchmarksAndTestsOnly) +public struct UndirectedGraph { + public init(nodes: [Node]) { self.nodes = nodes self.edges = .init(rows: nodes.count, columns: nodes.count) } @@ -22,7 +23,7 @@ struct UndirectedGraph { private var nodes: [Node] private var edges: AdjacencyMatrix - mutating func addEdge(source: Int, destination: Int) { + public mutating func addEdge(source: Int, destination: Int) { // Adjacency matrix is symmetrical for undirected graphs. self.edges[source, destination] = true self.edges[destination, source] = true @@ -33,7 +34,7 @@ struct UndirectedGraph { /// - source: `Index` of a node to start traversing edges from. /// - destination: `Index` of a node to which a connection could exist via edges from `source`. /// - Returns: `true` if a path from `source` to `destination` exists, `false` otherwise. - func areNodesConnected(source: Int, destination: Int) -> Bool { + public func areNodesConnected(source: Int, destination: Int) -> Bool { var todo = Deque([source]) var done = Set() diff --git a/Sources/Basics/Observability.swift b/Sources/Basics/Observability.swift index 2567c6be7ec..8a5afc23a70 100644 --- a/Sources/Basics/Observability.swift +++ b/Sources/Basics/Observability.swift @@ -56,6 +56,10 @@ public class ObservabilitySystem { self.underlying(scope, diagnostic) } } + + public static var NOOP: ObservabilityScope { + ObservabilitySystem { _, _ in }.topScope + } } public protocol ObservabilityHandlerProvider { diff --git a/Sources/PackageGraph/ModulesGraph.swift b/Sources/PackageGraph/ModulesGraph.swift index 1e5b0bb9690..8160fc5f84a 100644 --- a/Sources/PackageGraph/ModulesGraph.swift +++ b/Sources/PackageGraph/ModulesGraph.swift @@ -10,6 +10,8 @@ // //===----------------------------------------------------------------------===// +import protocol Basics.FileSystem +import class Basics.ObservabilityScope import struct Basics.IdentifiableSet import OrderedCollections import PackageLoading @@ -444,3 +446,50 @@ func topologicalSort( return result.reversed() } + +@_spi(DontAdoptOutsideOfSwiftPMExposedForBenchmarksAndTestsOnly) +public func loadModulesGraph( + identityResolver: IdentityResolver = DefaultIdentityResolver(), + fileSystem: FileSystem, + manifests: [Manifest], + binaryArtifacts: [PackageIdentity: [String: BinaryArtifact]] = [:], + explicitProduct: String? = .none, + shouldCreateMultipleTestProducts: Bool = false, + createREPLProduct: Bool = false, + useXCBuildFileRules: Bool = false, + customXCTestMinimumDeploymentTargets: [PackageModel.Platform: PlatformVersion]? = .none, + observabilityScope: ObservabilityScope +) throws -> ModulesGraph { + let rootManifests = manifests.filter(\.packageKind.isRoot).spm_createDictionary { ($0.path, $0) } + let externalManifests = try manifests.filter { !$0.packageKind.isRoot } + .reduce( + into: OrderedCollections + .OrderedDictionary() + ) { partial, item in + partial[try identityResolver.resolveIdentity(for: item.packageKind)] = (item, fileSystem) + } + + let packages = Array(rootManifests.keys) + let input = PackageGraphRootInput(packages: packages) + let graphRoot = PackageGraphRoot( + input: input, + manifests: rootManifests, + explicitProduct: explicitProduct, + observabilityScope: observabilityScope + ) + + return try ModulesGraph.load( + root: graphRoot, + identityResolver: identityResolver, + additionalFileRules: useXCBuildFileRules ? FileRuleDescription.xcbuildFileTypes : FileRuleDescription + .swiftpmFileTypes, + externalManifests: externalManifests, + binaryArtifacts: binaryArtifacts, + shouldCreateMultipleTestProducts: shouldCreateMultipleTestProducts, + createREPLProduct: createREPLProduct, + customXCTestMinimumDeploymentTargets: customXCTestMinimumDeploymentTargets, + availableLibraries: [], + fileSystem: fileSystem, + observabilityScope: observabilityScope + ) +} diff --git a/Sources/SPMTestSupport/MockPackageGraphs.swift b/Sources/SPMTestSupport/MockPackageGraphs.swift index 1f3f956e23a..00e12f77c19 100644 --- a/Sources/SPMTestSupport/MockPackageGraphs.swift +++ b/Sources/SPMTestSupport/MockPackageGraphs.swift @@ -13,7 +13,12 @@ import struct Basics.AbsolutePath import class Basics.ObservabilitySystem import class Basics.ObservabilityScope + import struct PackageGraph.ModulesGraph + +@_spi(DontAdoptOutsideOfSwiftPMExposedForBenchmarksAndTestsOnly) +import func PackageGraph.loadModulesGraph + import class PackageModel.Manifest import struct PackageModel.ProductDescription import struct PackageModel.TargetDescription diff --git a/Sources/SPMTestSupport/Observability.swift b/Sources/SPMTestSupport/Observability.swift index f997150e61a..c27349bf00d 100644 --- a/Sources/SPMTestSupport/Observability.swift +++ b/Sources/SPMTestSupport/Observability.swift @@ -24,10 +24,6 @@ extension ObservabilitySystem { let observabilitySystem = ObservabilitySystem(collector) return TestingObservability(collector: collector, topScope: observabilitySystem.topScope) } - - package static var NOOP: ObservabilityScope { - ObservabilitySystem { _, _ in }.topScope - } } package struct TestingObservability { diff --git a/Sources/SPMTestSupport/misc.swift b/Sources/SPMTestSupport/misc.swift index 8a36a09237d..5f22bf7e1e2 100644 --- a/Sources/SPMTestSupport/misc.swift +++ b/Sources/SPMTestSupport/misc.swift @@ -16,7 +16,10 @@ import struct Foundation.URL import class Foundation.Bundle #endif import OrderedCollections + +@_spi(DontAdoptOutsideOfSwiftPMExposedForBenchmarksAndTestsOnly) import PackageGraph + import PackageLoading import PackageModel import SourceControl @@ -347,52 +350,6 @@ package func loadPackageGraph( ) } -package func loadModulesGraph( - identityResolver: IdentityResolver = DefaultIdentityResolver(), - fileSystem: FileSystem, - manifests: [Manifest], - binaryArtifacts: [PackageIdentity: [String: BinaryArtifact]] = [:], - explicitProduct: String? = .none, - shouldCreateMultipleTestProducts: Bool = false, - createREPLProduct: Bool = false, - useXCBuildFileRules: Bool = false, - customXCTestMinimumDeploymentTargets: [PackageModel.Platform: PlatformVersion]? = .none, - observabilityScope: ObservabilityScope -) throws -> ModulesGraph { - let rootManifests = manifests.filter(\.packageKind.isRoot).spm_createDictionary { ($0.path, $0) } - let externalManifests = try manifests.filter { !$0.packageKind.isRoot } - .reduce( - into: OrderedCollections - .OrderedDictionary() - ) { partial, item in - partial[try identityResolver.resolveIdentity(for: item.packageKind)] = (item, fileSystem) - } - - let packages = Array(rootManifests.keys) - let input = PackageGraphRootInput(packages: packages) - let graphRoot = PackageGraphRoot( - input: input, - manifests: rootManifests, - explicitProduct: explicitProduct, - observabilityScope: observabilityScope - ) - - return try ModulesGraph.load( - root: graphRoot, - identityResolver: identityResolver, - additionalFileRules: useXCBuildFileRules ? FileRuleDescription.xcbuildFileTypes : FileRuleDescription - .swiftpmFileTypes, - externalManifests: externalManifests, - binaryArtifacts: binaryArtifacts, - shouldCreateMultipleTestProducts: shouldCreateMultipleTestProducts, - createREPLProduct: createREPLProduct, - customXCTestMinimumDeploymentTargets: customXCTestMinimumDeploymentTargets, - availableLibraries: [], - fileSystem: fileSystem, - observabilityScope: observabilityScope - ) -} - package let emptyZipFile = ByteString([0x80, 0x75, 0x05, 0x06] + [UInt8](repeating: 0x00, count: 18)) extension FileSystem { diff --git a/Tests/BasicsTests/Graph/DirectedGraphTests.swift b/Tests/BasicsTests/Graph/DirectedGraphTests.swift index a9f177d684a..3f168cb533c 100644 --- a/Tests/BasicsTests/Graph/DirectedGraphTests.swift +++ b/Tests/BasicsTests/Graph/DirectedGraphTests.swift @@ -10,7 +10,9 @@ // //===----------------------------------------------------------------------===// -@testable import Basics +@_spi(DontAdoptOutsideOfSwiftPMExposedForBenchmarksAndTestsOnly) +import Basics + import XCTest final class DirectedGraphTests: XCTestCase { diff --git a/Tests/BasicsTests/Graph/UndirectedGraphTests.swift b/Tests/BasicsTests/Graph/UndirectedGraphTests.swift index f5c0f812100..0ec595e6d91 100644 --- a/Tests/BasicsTests/Graph/UndirectedGraphTests.swift +++ b/Tests/BasicsTests/Graph/UndirectedGraphTests.swift @@ -10,7 +10,9 @@ // //===----------------------------------------------------------------------===// -@testable import Basics +@_spi(DontAdoptOutsideOfSwiftPMExposedForBenchmarksAndTestsOnly) +import Basics + import XCTest final class UndirectedGraphTests: XCTestCase { diff --git a/Tests/BuildTests/BuildPlanTests.swift b/Tests/BuildTests/BuildPlanTests.swift index 002e005b430..4a8b5072387 100644 --- a/Tests/BuildTests/BuildPlanTests.swift +++ b/Tests/BuildTests/BuildPlanTests.swift @@ -16,7 +16,9 @@ @testable import DriverSupport +@_spi(DontAdoptOutsideOfSwiftPMExposedForBenchmarksAndTestsOnly) @testable import PackageGraph + import PackageLoading @testable import PackageModel import SPMBuildCore diff --git a/Tests/BuildTests/LLBuildManifestBuilderTests.swift b/Tests/BuildTests/LLBuildManifestBuilderTests.swift index 69fd9b181ee..5ba597003d1 100644 --- a/Tests/BuildTests/LLBuildManifestBuilderTests.swift +++ b/Tests/BuildTests/LLBuildManifestBuilderTests.swift @@ -13,7 +13,10 @@ import Basics @testable import Build import LLBuildManifest + +@_spi(DontAdoptOutsideOfSwiftPMExposedForBenchmarksAndTestsOnly) import PackageGraph + import PackageModel import struct SPMBuildCore.BuildParameters diff --git a/Tests/BuildTests/ModuleAliasingBuildTests.swift b/Tests/BuildTests/ModuleAliasingBuildTests.swift index 6fb0b8d5d01..baa21707d21 100644 --- a/Tests/BuildTests/ModuleAliasingBuildTests.swift +++ b/Tests/BuildTests/ModuleAliasingBuildTests.swift @@ -12,7 +12,10 @@ import Basics @testable import Build + +@_spi(DontAdoptOutsideOfSwiftPMExposedForBenchmarksAndTestsOnly) @testable import PackageGraph + import PackageLoading @testable import PackageModel import SPMBuildCore diff --git a/Tests/BuildTests/ProductBuildDescriptionTests.swift b/Tests/BuildTests/ProductBuildDescriptionTests.swift index 444565cef7b..a314782fdae 100644 --- a/Tests/BuildTests/ProductBuildDescriptionTests.swift +++ b/Tests/BuildTests/ProductBuildDescriptionTests.swift @@ -22,7 +22,9 @@ import struct PackageModel.TargetDescription @testable import struct PackageGraph.ResolvedProduct -import func SPMTestSupport.loadModulesGraph +@_spi(DontAdoptOutsideOfSwiftPMExposedForBenchmarksAndTestsOnly) +import func PackageGraph.loadModulesGraph + import func SPMTestSupport.mockBuildParameters import func SPMTestSupport.XCTAssertNoDiagnostics import XCTest diff --git a/Tests/CommandsTests/MermaidPackageSerializerTests.swift b/Tests/CommandsTests/MermaidPackageSerializerTests.swift index f8524d28c04..2c5f88cd415 100644 --- a/Tests/CommandsTests/MermaidPackageSerializerTests.swift +++ b/Tests/CommandsTests/MermaidPackageSerializerTests.swift @@ -11,11 +11,14 @@ //===----------------------------------------------------------------------===// import class Basics.ObservabilitySystem + +@_spi(DontAdoptOutsideOfSwiftPMExposedForBenchmarksAndTestsOnly) +import func PackageGraph.loadModulesGraph + import class PackageModel.Manifest import struct PackageModel.ProductDescription import struct PackageModel.TargetDescription import class TSCBasic.InMemoryFileSystem -import func SPMTestSupport.loadModulesGraph import func SPMTestSupport.XCTAssertNoDiagnostics @testable diff --git a/Tests/CommandsTests/PackageCommandTests.swift b/Tests/CommandsTests/PackageCommandTests.swift index b5a673a6aad..d34e4c898fa 100644 --- a/Tests/CommandsTests/PackageCommandTests.swift +++ b/Tests/CommandsTests/PackageCommandTests.swift @@ -19,7 +19,10 @@ import CoreCommands import Commands import Foundation + +@_spi(DontAdoptOutsideOfSwiftPMExposedForBenchmarksAndTestsOnly) import PackageGraph + import PackageLoading import PackageModel import SourceControl diff --git a/Tests/CommandsTests/SwiftCommandStateTests.swift b/Tests/CommandsTests/SwiftCommandStateTests.swift index 071ee308448..bafa0b5f801 100644 --- a/Tests/CommandsTests/SwiftCommandStateTests.swift +++ b/Tests/CommandsTests/SwiftCommandStateTests.swift @@ -17,6 +17,10 @@ import CoreCommands @testable import Commands + +@_spi(DontAdoptOutsideOfSwiftPMExposedForBenchmarksAndTestsOnly) +import func PackageGraph.loadModulesGraph + @testable import PackageModel import SPMTestSupport import XCTest diff --git a/Tests/PackageGraphPerformanceTests/PackageGraphPerfTests.swift b/Tests/PackageGraphPerformanceTests/PackageGraphPerfTests.swift index 6073cc22896..78b0298697e 100644 --- a/Tests/PackageGraphPerformanceTests/PackageGraphPerfTests.swift +++ b/Tests/PackageGraphPerformanceTests/PackageGraphPerfTests.swift @@ -12,7 +12,10 @@ import Basics import OrderedCollections + +@_spi(DontAdoptOutsideOfSwiftPMExposedForBenchmarksAndTestsOnly) import PackageGraph + import PackageLoading import PackageModel import SPMTestSupport diff --git a/Tests/PackageGraphTests/ModulesGraphTests.swift b/Tests/PackageGraphTests/ModulesGraphTests.swift index 420387d5d7d..16c86130bbc 100644 --- a/Tests/PackageGraphTests/ModulesGraphTests.swift +++ b/Tests/PackageGraphTests/ModulesGraphTests.swift @@ -11,7 +11,10 @@ //===----------------------------------------------------------------------===// import Basics + +@_spi(DontAdoptOutsideOfSwiftPMExposedForBenchmarksAndTestsOnly) @testable import PackageGraph + import PackageModel import SPMTestSupport import XCTest diff --git a/Tests/XCBuildSupportTests/PIFBuilderTests.swift b/Tests/XCBuildSupportTests/PIFBuilderTests.swift index fa17ea9ebb8..65c2fe75065 100644 --- a/Tests/XCBuildSupportTests/PIFBuilderTests.swift +++ b/Tests/XCBuildSupportTests/PIFBuilderTests.swift @@ -12,7 +12,10 @@ import Basics import Foundation + +@_spi(DontAdoptOutsideOfSwiftPMExposedForBenchmarksAndTestsOnly) import PackageGraph + @testable import PackageModel import PackageLoading import SPMBuildCore From 1fa6298b3338cdd59c6148303591edeb4c82bf6f Mon Sep 17 00:00:00 2001 From: Euan Harris Date: Thu, 18 Apr 2024 09:18:38 +0100 Subject: [PATCH 092/159] pkgconfig: Apply PKG_CONFIG_SYSROOTDIR when generating paths (#7461) pkgconfig: Apply PKG_CONFIG_SYSROOTDIR when generating paths ### Motivation: SwiftPM's pkg-config implementation sets the `pc_sysrootdir` variable, but most `.pc` files do not use this variable directly. Instead, they rely on the pkg-config tool rewriting the generated paths to include the sysroot prefix when necessary. SwiftPM does not do this, so it does not generate the correct compiler flags to use libraries from a sysroot. This problem was reported in issue https://github.com/apple/swift-package-manager/issues/7409 ### Modifications: There are two major pkg-config implementations which handle sysroot differently: * `pkg-config` (the original https://pkg-config.freedesktop.org implementation) prepends sysroot after variable expansion, when it creates the compiler flag lists * `pkgconf` (the newer http://pkgconf.org implementation) prepends sysroot to variables when they are defined, so sysroot is included when they are expanded `pkg-config`'s method skips single character compiler flags, such as `-I` and `-L`, and has special cases for longer options. It does not handle spaces between the flags and their values properly, and prepends sysroot multiple times in some cases, such as when the .pc file uses the `sysroot_dir` variable directly or has been rewritten to hard-code the sysroot prefix. `pkgconf`'s method handles spaces correctly, although it also makes extra checks to ensure that sysroot is not applied more than once. In 2024 `pkg-config` is the more popular option according to Homebrew installation statistics, but the major Linux distributions have generally switched to `pkgconf`. We will use `pkgconf`'s method here as it seems more robust than `pkg-config`'s, and `pkgconf`'s greater popularity on Linux means libraries developed there may depend on the specific way it handles `.pc` files. ### Result: SwiftPM will now apply the sysroot prefix to compiler flags, such as include (`-I`) and library (`-L`) search paths. This is a partial fix for https://github.com/apple/swift-package-manager/issues/7409. The sysroot prefix is only applied when the `PKG_CONFIG_SYSROOT_DIR` environment variable is set. A future commit could apply an appropriate sysroot automatically when the `--experimental-swift-sdk` flag is used. --- Sources/PackageLoading/PkgConfig.swift | 94 ++++++++++++++++++- .../PkgConfigParserTests.swift | 84 ++++++++++++++++- .../pkgconfigInputs/case_insensitive.pc | 3 + .../pkgconfigInputs/deps_variable.pc | 3 + .../pkgconfigInputs/double_sysroot.pc | 7 ++ .../pkgconfigInputs/dummy_dependency.pc | 3 + .../pkgconfigInputs/empty_cflags.pc | 3 + .../pkgconfigInputs/escaped_spaces.pc | 3 + .../pkgconfigInputs/failure_case.pc | 3 + .../pkgconfigInputs/not_double_sysroot.pc | 6 ++ .../pkgconfigInputs/quotes_failure.pc | 3 + 11 files changed, 206 insertions(+), 6 deletions(-) create mode 100644 Tests/PackageLoadingTests/pkgconfigInputs/double_sysroot.pc create mode 100644 Tests/PackageLoadingTests/pkgconfigInputs/not_double_sysroot.pc diff --git a/Sources/PackageLoading/PkgConfig.swift b/Sources/PackageLoading/PkgConfig.swift index 2fdb1d60788..8838be90135 100644 --- a/Sources/PackageLoading/PkgConfig.swift +++ b/Sources/PackageLoading/PkgConfig.swift @@ -47,6 +47,7 @@ public struct PkgConfig { name: String, additionalSearchPaths: [AbsolutePath]? = .none, brewPrefix: AbsolutePath? = .none, + sysrootDir: AbsolutePath? = .none, fileSystem: FileSystem, observabilityScope: ObservabilityScope ) throws { @@ -54,6 +55,7 @@ public struct PkgConfig { name: name, additionalSearchPaths: additionalSearchPaths ?? [], brewPrefix: brewPrefix, + sysrootDir: sysrootDir, loadingContext: LoadingContext(), fileSystem: fileSystem, observabilityScope: observabilityScope @@ -64,6 +66,7 @@ public struct PkgConfig { name: String, additionalSearchPaths: [AbsolutePath], brewPrefix: AbsolutePath?, + sysrootDir: AbsolutePath?, loadingContext: LoadingContext, fileSystem: FileSystem, observabilityScope: ObservabilityScope @@ -85,7 +88,7 @@ public struct PkgConfig { ) } - var parser = try PkgConfigParser(pcFile: pcFile, fileSystem: fileSystem) + var parser = try PkgConfigParser(pcFile: pcFile, fileSystem: fileSystem, sysrootDir: ProcessEnv.block["PKG_CONFIG_SYSROOT_DIR"]) try parser.parse() func getFlags(from dependencies: [String]) throws -> (cFlags: [String], libs: [String]) { @@ -103,6 +106,7 @@ public struct PkgConfig { name: dep, additionalSearchPaths: additionalSearchPaths, brewPrefix: brewPrefix, + sysrootDir: sysrootDir, loadingContext: loadingContext, fileSystem: fileSystem, observabilityScope: observabilityScope @@ -162,13 +166,93 @@ internal struct PkgConfigParser { public private(set) var privateDependencies = [String]() public private(set) var cFlags = [String]() public private(set) var libs = [String]() + public private(set) var sysrootDir: String? - public init(pcFile: AbsolutePath, fileSystem: FileSystem) throws { + public init(pcFile: AbsolutePath, fileSystem: FileSystem, sysrootDir: String?) throws { guard fileSystem.isFile(pcFile) else { throw StringError("invalid pcfile \(pcFile)") } self.pcFile = pcFile self.fileSystem = fileSystem + self.sysrootDir = sysrootDir + } + + // Compress repeated path separators to one. + private func compressPathSeparators(_ value: String) -> String { + let components = value.components(separatedBy: "/").filter { !$0.isEmpty }.joined(separator: "/") + if value.hasPrefix("/") { + return "/" + components + } else { + return components + } + } + + // Trim duplicate sysroot prefixes, matching the approach of pkgconf + private func trimDuplicateSysroot(_ value: String) -> String { + // If sysroot has been applied more than once, remove the first instance. + // pkgconf makes this check after variable expansion to handle rare .pc + // files which expand ${pc_sysrootdir} directly: + // https://github.com/pkgconf/pkgconf/issues/123 + // + // For example: + // /sysroot/sysroot/remainder -> /sysroot/remainder + // + // However, pkgconf's algorithm searches for an additional sysrootdir anywhere in + // the string after the initial prefix, rather than looking for two sysrootdir prefixes + // directly next to each other: + // + // /sysroot/filler/sysroot/remainder -> /filler/sysroot/remainder + // + // It might seem more logical not to strip sysroot in this case, as it is not a double + // prefix, but for compatibility trimDuplicateSysroot is faithful to pkgconf's approach + // in the functions `pkgconf_tuple_parse` and `should_rewrite_sysroot`. + + // Only trim if sysroot is defined with a meaningful value + guard let sysrootDir, sysrootDir != "/" else { + return value + } + + // Only trim absolute paths starting with sysroot + guard value.hasPrefix("/"), value.hasPrefix(sysrootDir) else { + return value + } + + // If sysroot appears multiple times, trim the prefix + // N.B. sysroot can appear anywhere in the remainder + // of the value, mirroring pkgconf's logic + let pathSuffix = value.dropFirst(sysrootDir.count) + if pathSuffix.contains(sysrootDir) { + return String(pathSuffix) + } else { + return value + } + } + + // Apply sysroot to generated paths, matching the approach of pkgconf + private func applySysroot(_ value: String) -> String { + // The two main pkg-config implementations handle sysroot differently: + // + // `pkg-config` (freedesktop.org) prepends sysroot after variable expansion, when in creates the compiler flag lists + // `pkgconf` prepends sysroot to variables when they are defined, so sysroot is included when they are expanded + // + // pkg-config's method skips single character compiler flags, such as '-I' and '-L', and has special cases for longer options. + // It does not handle spaces between the flags and their values properly, and prepends sysroot multiple times in some cases, + // such as when the .pc file uses the sysroot_dir variable directly or has been rewritten to hard-code the sysroot prefix. + // + // pkgconf's method handles spaces correctly, although it also requires extra checks to ensure that sysroot is not applied + // more than once. + // + // In 2024 pkg-config is the more popular option according to Homebrew installation statistics, but the major Linux distributions + // have generally switched to pkgconf. + // + // We will use pkgconf's method here as it seems more robust than pkg-config's, and pkgconf's greater popularity on Linux + // means libraries developed there may depend on the specific way it handles .pc files. + + if value.hasPrefix("/"), let sysrootDir, !value.hasPrefix(sysrootDir) { + return compressPathSeparators(trimDuplicateSysroot(sysrootDir + value)) + } else { + return compressPathSeparators(trimDuplicateSysroot(value)) + } } public mutating func parse() throws { @@ -183,7 +267,9 @@ internal struct PkgConfigParser { variables["pcfiledir"] = pcFile.parentDirectory.pathString // Add pc_sysrootdir variable. This is the path of the sysroot directory for pc files. - variables["pc_sysrootdir"] = ProcessEnv.block["PKG_CONFIG_SYSROOT_DIR"] ?? AbsolutePath.root.pathString + // pkgconf does not define pc_sysrootdir if the path of the .pc file is outside sysrootdir. + // SwiftPM does not currently make that check. + variables["pc_sysrootdir"] = sysrootDir ?? AbsolutePath.root.pathString let fileContents: String = try fileSystem.readFileContents(pcFile) for line in fileContents.components(separatedBy: "\n") { @@ -199,7 +285,7 @@ internal struct PkgConfigParser { // Found a variable. let (name, maybeValue) = line.spm_split(around: "=") let value = maybeValue?.spm_chuzzle() ?? "" - variables[name.spm_chuzzle() ?? ""] = try resolveVariables(value) + variables[name.spm_chuzzle() ?? ""] = try applySysroot(resolveVariables(value)) } else { // Unexpected thing in the pc file, abort. throw PkgConfigError.parsingError("Unexpected line: \(line) in \(pcFile)") diff --git a/Tests/PackageLoadingTests/PkgConfigParserTests.swift b/Tests/PackageLoadingTests/PkgConfigParserTests.swift index 009f1328ff4..110bf941ff7 100644 --- a/Tests/PackageLoadingTests/PkgConfigParserTests.swift +++ b/Tests/PackageLoadingTests/PkgConfigParserTests.swift @@ -244,12 +244,92 @@ final class PkgConfigParserTests: XCTestCase { } } + func testSysrootDir() throws { + // sysroot should be prepended to all path variables, and should therefore appear in cflags and libs. + try loadPCFile("gtk+-3.0.pc", sysrootDir: "/opt/sysroot/somewhere") { parser in + XCTAssertEqual(parser.variables, [ + "libdir": "/opt/sysroot/somewhere/usr/local/Cellar/gtk+3/3.18.9/lib", + "gtk_host": "x86_64-apple-darwin15.3.0", + "includedir": "/opt/sysroot/somewhere/usr/local/Cellar/gtk+3/3.18.9/include", + "prefix": "/opt/sysroot/somewhere/usr/local/Cellar/gtk+3/3.18.9", + "gtk_binary_version": "3.0.0", + "exec_prefix": "/opt/sysroot/somewhere/usr/local/Cellar/gtk+3/3.18.9", + "targets": "quartz", + "pcfiledir": parser.pcFile.parentDirectory.pathString, + "pc_sysrootdir": "/opt/sysroot/somewhere" + ]) + XCTAssertEqual(parser.dependencies, ["gdk-3.0", "atk", "cairo", "cairo-gobject", "gdk-pixbuf-2.0", "gio-2.0"]) + XCTAssertEqual(parser.privateDependencies, ["atk", "epoxy", "gio-unix-2.0"]) + XCTAssertEqual(parser.cFlags, ["-I/opt/sysroot/somewhere/usr/local/Cellar/gtk+3/3.18.9/include/gtk-3.0"]) + XCTAssertEqual(parser.libs, ["-L/opt/sysroot/somewhere/usr/local/Cellar/gtk+3/3.18.9/lib", "-lgtk-3"]) + } + + // sysroot should be not be prepended if it is already a prefix + // - pkgconf makes this check, but pkg-config does not + // - If the .pc file lies outside sysrootDir, pkgconf sets pc_sysrootdir to the empty string + // https://github.com/pkgconf/pkgconf/issues/213 + // SwiftPM does not currently implement this special case. + try loadPCFile("gtk+-3.0.pc", sysrootDir: "/usr/local/Cellar") { parser in + XCTAssertEqual(parser.variables, [ + "libdir": "/usr/local/Cellar/gtk+3/3.18.9/lib", + "gtk_host": "x86_64-apple-darwin15.3.0", + "includedir": "/usr/local/Cellar/gtk+3/3.18.9/include", + "prefix": "/usr/local/Cellar/gtk+3/3.18.9", + "gtk_binary_version": "3.0.0", + "exec_prefix": "/usr/local/Cellar/gtk+3/3.18.9", + "targets": "quartz", + "pcfiledir": parser.pcFile.parentDirectory.pathString, + "pc_sysrootdir": "/usr/local/Cellar" + ]) + XCTAssertEqual(parser.dependencies, ["gdk-3.0", "atk", "cairo", "cairo-gobject", "gdk-pixbuf-2.0", "gio-2.0"]) + XCTAssertEqual(parser.privateDependencies, ["atk", "epoxy", "gio-unix-2.0"]) + XCTAssertEqual(parser.cFlags, ["-I/usr/local/Cellar/gtk+3/3.18.9/include/gtk-3.0"]) + XCTAssertEqual(parser.libs, ["-L/usr/local/Cellar/gtk+3/3.18.9/lib", "-lgtk-3"]) + } + + // sysroot should be not be double-prepended if it is used explicitly by the .pc file + // - pkgconf makes this check, but pkg-config does not + try loadPCFile("double_sysroot.pc", sysrootDir: "/sysroot") { parser in + XCTAssertEqual(parser.variables, [ + "prefix": "/sysroot/usr", + "datarootdir": "/sysroot/usr/share", + "pkgdatadir": "/sysroot/usr/share/pkgdata", + "pcfiledir": parser.pcFile.parentDirectory.pathString, + "pc_sysrootdir": "/sysroot" + ]) + } + + // pkgconfig strips a leading sysroot prefix if sysroot appears anywhere else in the + // expanded variable. SwiftPM's implementation is faithful to pkgconfig, even + // thought it might seem more logical not to strip the prefix in this case. + try loadPCFile("not_double_sysroot.pc", sysrootDir: "/sysroot") { parser in + XCTAssertEqual(parser.variables, [ + "prefix": "/sysroot/usr", + "datarootdir": "/sysroot/usr/share", + "pkgdatadir": "/filler/sysroot/usr/share/pkgdata", + "pcfiledir": parser.pcFile.parentDirectory.pathString, + "pc_sysrootdir": "/sysroot" + ]) + } + + // pkgconfig does not strip sysroot if it is a relative path + try loadPCFile("double_sysroot.pc", sysrootDir: "sysroot") { parser in + XCTAssertEqual(parser.variables, [ + "prefix": "sysroot/usr", + "datarootdir": "sysroot/usr/share", + "pkgdatadir": "sysroot/sysroot/usr/share/pkgdata", + "pcfiledir": parser.pcFile.parentDirectory.pathString, + "pc_sysrootdir": "sysroot" + ]) + } + } + private func pcFilePath(_ inputName: String) -> AbsolutePath { return AbsolutePath(#file).parentDirectory.appending(components: "pkgconfigInputs", inputName) } - private func loadPCFile(_ inputName: String, body: ((PkgConfigParser) -> Void)? = nil) throws { - var parser = try PkgConfigParser(pcFile: pcFilePath(inputName), fileSystem: localFileSystem) + private func loadPCFile(_ inputName: String, sysrootDir: String? = nil, body: ((PkgConfigParser) -> Void)? = nil) throws { + var parser = try PkgConfigParser(pcFile: pcFilePath(inputName), fileSystem: localFileSystem, sysrootDir: sysrootDir) try parser.parse() body?(parser) } diff --git a/Tests/PackageLoadingTests/pkgconfigInputs/case_insensitive.pc b/Tests/PackageLoadingTests/pkgconfigInputs/case_insensitive.pc index b65a14c7d16..83017682ea9 100644 --- a/Tests/PackageLoadingTests/pkgconfigInputs/case_insensitive.pc +++ b/Tests/PackageLoadingTests/pkgconfigInputs/case_insensitive.pc @@ -2,6 +2,9 @@ prefix=/usr/local/bin exec_prefix=${prefix} #some comment +Name: case_insensitive +Version: 1 +Description: Demonstrate that pkgconfig keys are case-insensitive # upstream pkg-config parser allows Cflags & CFlags as key CFlags: -I/usr/local/include diff --git a/Tests/PackageLoadingTests/pkgconfigInputs/deps_variable.pc b/Tests/PackageLoadingTests/pkgconfigInputs/deps_variable.pc index 225d86d1369..de06c50db85 100644 --- a/Tests/PackageLoadingTests/pkgconfigInputs/deps_variable.pc +++ b/Tests/PackageLoadingTests/pkgconfigInputs/deps_variable.pc @@ -2,6 +2,9 @@ prefix=/usr/local/bin exec_prefix=${prefix} my_dep=atk #some comment +Name: deps_variable +Version: 1 +Description: Demonstrate use of a locally-defined variable Requires: gdk-3.0 >= 1.0.0 ${my_dep} Libs: -L${prefix} -lgtk-3 diff --git a/Tests/PackageLoadingTests/pkgconfigInputs/double_sysroot.pc b/Tests/PackageLoadingTests/pkgconfigInputs/double_sysroot.pc new file mode 100644 index 00000000000..be7abee01ce --- /dev/null +++ b/Tests/PackageLoadingTests/pkgconfigInputs/double_sysroot.pc @@ -0,0 +1,7 @@ +prefix=/usr +datarootdir=${prefix}/share +pkgdatadir=${pc_sysrootdir}/${datarootdir}/pkgdata + +Name: double_sysroot +Description: Demonstrate double-prefixing of pc_sysrootdir (https://github.com/pkgconf/pkgconf/issues/123) +Version: 1 diff --git a/Tests/PackageLoadingTests/pkgconfigInputs/dummy_dependency.pc b/Tests/PackageLoadingTests/pkgconfigInputs/dummy_dependency.pc index c4ae253c81a..e3a797923da 100644 --- a/Tests/PackageLoadingTests/pkgconfigInputs/dummy_dependency.pc +++ b/Tests/PackageLoadingTests/pkgconfigInputs/dummy_dependency.pc @@ -2,6 +2,9 @@ prefix=/usr/local/bin exec_prefix=${prefix} #some comment +Name: dummy_dependency +Version: 1 +Description: Demonstrate a blank dependency entry Requires: pango, , fontconfig >= 2.13.0 Libs:-L${prefix} -lpangoft2-1.0 diff --git a/Tests/PackageLoadingTests/pkgconfigInputs/empty_cflags.pc b/Tests/PackageLoadingTests/pkgconfigInputs/empty_cflags.pc index 0f7f8e6c1a2..a7d2ffa6bae 100644 --- a/Tests/PackageLoadingTests/pkgconfigInputs/empty_cflags.pc +++ b/Tests/PackageLoadingTests/pkgconfigInputs/empty_cflags.pc @@ -2,6 +2,9 @@ prefix=/usr/local/bin exec_prefix=${prefix} #some comment +Name: empty_cflags +Version: 1 +Description: Demonstrate an empty cflags list Requires: gdk-3.0 atk Libs:-L${prefix} -lgtk-3 diff --git a/Tests/PackageLoadingTests/pkgconfigInputs/escaped_spaces.pc b/Tests/PackageLoadingTests/pkgconfigInputs/escaped_spaces.pc index f00283e070f..e0e77e73774 100644 --- a/Tests/PackageLoadingTests/pkgconfigInputs/escaped_spaces.pc +++ b/Tests/PackageLoadingTests/pkgconfigInputs/escaped_spaces.pc @@ -2,6 +2,9 @@ prefix=/usr/local/bin exec_prefix=${prefix} my_dep=atk #some comment +Name: escaped_spaces +Version: 1 +Description: Demonstrate use of escape characters in flag values Requires: gdk-3.0 >= 1.0.0 ${my_dep} Libs: -L"${prefix}" -l"gtk 3" -wantareal\\here -one\\ -two diff --git a/Tests/PackageLoadingTests/pkgconfigInputs/failure_case.pc b/Tests/PackageLoadingTests/pkgconfigInputs/failure_case.pc index e81dc924502..f1981c1b3d4 100644 --- a/Tests/PackageLoadingTests/pkgconfigInputs/failure_case.pc +++ b/Tests/PackageLoadingTests/pkgconfigInputs/failure_case.pc @@ -2,6 +2,9 @@ prefix=/usr/local/bin exec_prefix=${prefix} #some comment +Name: failure_case +Version: 1 +Description: Demonstrate failure caused by use of an undefined variable Requires: gdk-3.0 >= 1.0.0 Libs: -L${prefix} -lgtk-3 ${my_dep} diff --git a/Tests/PackageLoadingTests/pkgconfigInputs/not_double_sysroot.pc b/Tests/PackageLoadingTests/pkgconfigInputs/not_double_sysroot.pc new file mode 100644 index 00000000000..c537069beb6 --- /dev/null +++ b/Tests/PackageLoadingTests/pkgconfigInputs/not_double_sysroot.pc @@ -0,0 +1,6 @@ +prefix=/usr +datarootdir=${prefix}/share +pkgdatadir=${pc_sysrootdir}/filler/${datarootdir}/pkgdata + +Name: double_sysroot +Description: Demonstrate pc_sysrootdir appearing elsewhere in a path - this is not a double prefix and should not be removed \ No newline at end of file diff --git a/Tests/PackageLoadingTests/pkgconfigInputs/quotes_failure.pc b/Tests/PackageLoadingTests/pkgconfigInputs/quotes_failure.pc index 3006c2fbd85..07ec4503a76 100644 --- a/Tests/PackageLoadingTests/pkgconfigInputs/quotes_failure.pc +++ b/Tests/PackageLoadingTests/pkgconfigInputs/quotes_failure.pc @@ -2,6 +2,9 @@ prefix=/usr/local/bin exec_prefix=${prefix} my_dep=atk #some comment +Name: quotes_failure +Version: 1 +Description: Demonstrate failure due to unbalanced quotes Requires: gdk-3.0 >= 1.0.0 ${my_dep} Libs: -L"${prefix}" -l"gt"k3" -wantareal\\here -one\\ -two From c7fba15082253d4ff9b45a9a395345821ea25cad Mon Sep 17 00:00:00 2001 From: Ben Barham Date: Thu, 18 Apr 2024 03:52:20 -0700 Subject: [PATCH 093/159] Use new SPI for SourceKitLSPAPITests (#7468) SPI was added here in #7465, but missed adding it to this module. --------- Co-authored-by: Max Desiatov --- Tests/SPMBuildCoreTests/PluginInvocationTests.swift | 3 +++ Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift | 3 +++ 2 files changed, 6 insertions(+) diff --git a/Tests/SPMBuildCoreTests/PluginInvocationTests.swift b/Tests/SPMBuildCoreTests/PluginInvocationTests.swift index 6f35776a211..a0deb0d6652 100644 --- a/Tests/SPMBuildCoreTests/PluginInvocationTests.swift +++ b/Tests/SPMBuildCoreTests/PluginInvocationTests.swift @@ -11,7 +11,10 @@ //===----------------------------------------------------------------------===// import Basics + +@_spi(DontAdoptOutsideOfSwiftPMExposedForBenchmarksAndTestsOnly) @testable import PackageGraph + import PackageLoading import PackageModel @testable import SPMBuildCore diff --git a/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift b/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift index 759106a8fa6..0ef54fbc308 100644 --- a/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift +++ b/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift @@ -12,7 +12,10 @@ import Basics import Build + +@_spi(DontAdoptOutsideOfSwiftPMExposedForBenchmarksAndTestsOnly) import PackageGraph + import PackageModel import SourceKitLSPAPI import SPMTestSupport From b5565631b9f6bef8e30d2c7958062406a1c1b750 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 18 Apr 2024 11:54:04 +0100 Subject: [PATCH 094/159] Add synthetic modules-with-macros graph benchmarks (#7466) Benchmarking modules graph is now generalized with `syntheticModulesGraph` function. It also uncovered a bug in the previous `SyntheticModulesGraph`, which didn't pass generated `TargetDescription`s array to `loadModulesGraph`, which is fixed now. New `SyntheticModulesGraphWithMacros` calls `syntheticModulesGraph` with `includeMacros: true` argument, which splits all modules in three parts: library modules, macros modules that library modules depend on, and macro dependencies that macros depend on. This allows us to track potential performance regressions in https://github.com/apple/swift-package-manager/pull/7353. --- .../PackageGraphBenchmarks.swift | 137 +++++++++++++----- Benchmarks/README.md | 2 +- ...arks.SwiftPMWorkspaceModulesGraph.p90.json | 4 + ...hBenchmarks.SyntheticModulesGraph.p90.json | 4 + ...s.SyntheticModulesGraphWithMacros.p90.json | 4 + 5 files changed, 113 insertions(+), 38 deletions(-) create mode 100644 Benchmarks/Thresholds/macos-arm64/PackageGraphBenchmarks.SwiftPMWorkspaceModulesGraph.p90.json create mode 100644 Benchmarks/Thresholds/macos-arm64/PackageGraphBenchmarks.SyntheticModulesGraph.p90.json create mode 100644 Benchmarks/Thresholds/macos-arm64/PackageGraphBenchmarks.SyntheticModulesGraphWithMacros.p90.json diff --git a/Benchmarks/Benchmarks/PackageGraphBenchmarks/PackageGraphBenchmarks.swift b/Benchmarks/Benchmarks/PackageGraphBenchmarks/PackageGraphBenchmarks.swift index 1c4d38061e4..bab1f4af582 100644 --- a/Benchmarks/Benchmarks/PackageGraphBenchmarks/PackageGraphBenchmarks.swift +++ b/Benchmarks/Benchmarks/PackageGraphBenchmarks/PackageGraphBenchmarks.swift @@ -27,7 +27,7 @@ let benchmarks = { let parsedValue = Int(envVar) { modulesGraphDepth = parsedValue } else { - modulesGraphDepth = 100 + modulesGraphDepth = 150 } let modulesGraphWidth: Int @@ -35,7 +35,7 @@ let benchmarks = { let parsedValue = Int(envVar) { modulesGraphWidth = parsedValue } else { - modulesGraphWidth = 100 + modulesGraphWidth = 150 } let packagesGraphDepth: Int @@ -46,8 +46,6 @@ let benchmarks = { packagesGraphDepth = 10 } - let noopObservability = ObservabilitySystem.NOOP - // Benchmarks computation of a resolved graph of modules for a package using `Workspace` as an entry point. It runs PubGrub to get // resolved concrete versions of dependencies, assigning all modules and products to each other as corresponding dependencies // with their build triples, but with the build plan not yet constructed. In this benchmark specifically we're loading `Package.swift` @@ -67,55 +65,120 @@ let benchmarks = { let workspace = try Workspace(fileSystem: localFileSystem, location: .init(forRootPackage: path, fileSystem: localFileSystem)) for _ in benchmark.scaledIterations { - try workspace.loadPackageGraph(rootPath: path, observabilityScope: noopObservability) + try workspace.loadPackageGraph(rootPath: path, observabilityScope: ObservabilitySystem.NOOP) } } - - // Benchmarks computation of a resolved graph of modules for a synthesized package using `loadModulesGraph` as an - // entry point, which almost immediately delegates to `ModulesGraph.load` under the hood. + // Benchmarks computation of a resolved graph of modules for a trivial synthesized package using `loadModulesGraph` + // as an entry point, which almost immediately delegates to `ModulesGraph.load` under the hood. Benchmark( "SyntheticModulesGraph", configuration: .init( metrics: defaultMetrics, maxDuration: .seconds(10), thresholds: [ - .mallocCountTotal: .init(absolute: [.p90: 2500]), - .syscalls: .init(absolute: [.p90: 0]), + .mallocCountTotal: .init(absolute: [.p90: 17000]), + .syscalls: .init(absolute: [.p90: 5]), ] ) ) { benchmark in - let targets = try (0.. TargetDescription in + let dependencies = (0.. [TargetDescription.Dependency] in + if includeMacros { + [.target(name: "Module\(i)"), .target(name: "Macros\(i)")] + } else { + [.target(name: "Module\(i)")] + } + } + return try TargetDescription(name: "Module\(i)", dependencies: dependencies) + } + + let macrosModules: [TargetDescription] + let macrosDependenciesModules: [TargetDescription] + if includeMacros { + macrosModules = try (0.. Date: Mon, 22 Apr 2024 06:25:07 -0700 Subject: [PATCH 095/159] [SE-0301] Updating a project and its manifest programmatically and from the command line (#7467) --- BuildSupport/SwiftSyntax/CMakeLists.txt | 17 + CMakeLists.txt | 1 + Package.swift | 30 + Sources/CMakeLists.txt | 1 + Sources/Commands/CMakeLists.txt | 3 + .../PackageCommands/AddDependency.swift | 155 ++++ .../Commands/PackageCommands/AddTarget.swift | 128 ++++ .../PackageCommands/SwiftPackageCommand.swift | 2 + .../Manifest/TargetDescription.swift | 2 +- .../ManifestSourceGeneration.swift | 2 +- .../AddPackageDependency.swift | 55 ++ Sources/PackageModelSyntax/AddTarget.swift | 227 ++++++ Sources/PackageModelSyntax/CMakeLists.txt | 61 ++ .../ManifestEditError.swift | 53 ++ .../ManifestSyntaxRepresentable.swift | 46 ++ .../PackageDependency+Syntax.swift | 93 +++ .../PackageEditResult.swift | 97 +++ .../PackageModelSyntax/SyntaxEditUtils.swift | 462 ++++++++++++ .../TargetDescription+Syntax.swift | 99 +++ Sources/Workspace/Workspace.swift | 2 +- Tests/CommandsTests/PackageCommandTests.swift | 74 +- .../FunctionalTests/MiscellaneousTests.swift | 2 +- Tests/FunctionalTests/PluginTests.swift | 4 +- Tests/FunctionalTests/ResourcesTests.swift | 2 +- .../ManifestEditTests.swift | 666 ++++++++++++++++++ 25 files changed, 2269 insertions(+), 15 deletions(-) create mode 100644 BuildSupport/SwiftSyntax/CMakeLists.txt create mode 100644 Sources/Commands/PackageCommands/AddDependency.swift create mode 100644 Sources/Commands/PackageCommands/AddTarget.swift create mode 100644 Sources/PackageModelSyntax/AddPackageDependency.swift create mode 100644 Sources/PackageModelSyntax/AddTarget.swift create mode 100644 Sources/PackageModelSyntax/CMakeLists.txt create mode 100644 Sources/PackageModelSyntax/ManifestEditError.swift create mode 100644 Sources/PackageModelSyntax/ManifestSyntaxRepresentable.swift create mode 100644 Sources/PackageModelSyntax/PackageDependency+Syntax.swift create mode 100644 Sources/PackageModelSyntax/PackageEditResult.swift create mode 100644 Sources/PackageModelSyntax/SyntaxEditUtils.swift create mode 100644 Sources/PackageModelSyntax/TargetDescription+Syntax.swift create mode 100644 Tests/PackageModelSyntaxTests/ManifestEditTests.swift diff --git a/BuildSupport/SwiftSyntax/CMakeLists.txt b/BuildSupport/SwiftSyntax/CMakeLists.txt new file mode 100644 index 00000000000..7a3cfbfd4de --- /dev/null +++ b/BuildSupport/SwiftSyntax/CMakeLists.txt @@ -0,0 +1,17 @@ +SET(SWIFTPM_PATH_TO_SWIFT_SYNTAX_SOURCE ${CMAKE_SOURCE_DIR}/../swift-syntax) +message(STATUS "swift-syntax path: ${SWIFTPM_PATH_TO_SWIFT_SYNTAX_SOURCE}") + +include(FetchContent) + +if(NOT EXISTS "${SWIFTPM_PATH_TO_SWIFT_SYNTAX_SOURCE}") + message(SEND_ERROR "swift-syntax is required to build SwiftPM. Please run update-checkout or specify SWIFTPM_PATH_TO_SWIFT_SYNTAX_SOURCE") + return() +endif() + +# Build swift-syntax libraries with FetchContent. +# set(CMAKE_Swift_COMPILER_TARGET ${SWIFT_HOST_TRIPLE}) +set(BUILD_SHARED_LIBS OFF) + +file(TO_CMAKE_PATH "${SWIFTPM_PATH_TO_SWIFT_SYNTAX_SOURCE}" swift_syntax_path) +FetchContent_Declare(SwiftSyntax SOURCE_DIR "${swift_syntax_path}") +FetchContent_MakeAvailable(SwiftSyntax) diff --git a/CMakeLists.txt b/CMakeLists.txt index 029f3ffa337..1a9153193ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,5 +54,6 @@ find_package(SQLite3 REQUIRED) # Enable `package` modifier for the whole package. add_compile_options("$<$:-package-name;SwiftPM>") +add_subdirectory(BuildSupport/SwiftSyntax) add_subdirectory(Sources) add_subdirectory(cmake/modules) diff --git a/Package.swift b/Package.swift index 95a74ccc320..b48a7debc47 100644 --- a/Package.swift +++ b/Package.swift @@ -49,6 +49,7 @@ let swiftPMDataModelProduct = ( "PackageLoading", "PackageMetadata", "PackageModel", + "PackageModelSyntax", "SourceControl", "Workspace", ] @@ -246,6 +247,23 @@ let package = Package( swiftSettings: packageModelResourcesSettings ), + .target( + /** Primary Package model objects relationship to SwiftSyntax */ + name: "PackageModelSyntax", + dependencies: [ + "Basics", + "PackageLoading", + "PackageModel", + .product(name: "SwiftBasicFormat", package: "swift-syntax"), + .product(name: "SwiftDiagnostics", package: "swift-syntax"), + .product(name: "SwiftIDEUtils", package: "swift-syntax"), + .product(name: "SwiftParser", package: "swift-syntax"), + .product(name: "SwiftSyntax", package: "swift-syntax"), + .product(name: "SwiftSyntaxBuilder", package: "swift-syntax"), + ], + exclude: ["CMakeLists.txt"] + ), + .target( /** Package model conventions and loading support */ name: "PackageLoading", @@ -414,10 +432,12 @@ let package = Package( dependencies: [ .product(name: "ArgumentParser", package: "swift-argument-parser"), .product(name: "OrderedCollections", package: "swift-collections"), + .product(name: "SwiftIDEUtils", package: "swift-syntax"), "Basics", "Build", "CoreCommands", "PackageGraph", + "PackageModelSyntax", "SourceControl", "Workspace", "XCBuildSupport", @@ -635,6 +655,14 @@ let package = Package( name: "PackageModelTests", dependencies: ["PackageModel", "SPMTestSupport"] ), + .testTarget( + name: "PackageModelSyntaxTests", + dependencies: [ + "PackageModelSyntax", + "SPMTestSupport", + .product(name: "SwiftIDEUtils", package: "swift-syntax"), + ] + ), .testTarget( name: "PackageGraphTests", dependencies: ["PackageGraph", "SPMTestSupport"] @@ -785,6 +813,7 @@ if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil { .package(url: "https://github.com/apple/swift-argument-parser.git", .upToNextMinor(from: "1.2.2")), .package(url: "https://github.com/apple/swift-driver.git", branch: relatedDependenciesBranch), .package(url: "https://github.com/apple/swift-crypto.git", .upToNextMinor(from: "3.0.0")), + .package(url: "https://github.com/apple/swift-syntax.git", branch: relatedDependenciesBranch), .package(url: "https://github.com/apple/swift-system.git", .upToNextMinor(from: "1.1.1")), .package(url: "https://github.com/apple/swift-collections.git", .upToNextMinor(from: "1.0.1")), .package(url: "https://github.com/apple/swift-certificates.git", .upToNextMinor(from: "1.0.1")), @@ -795,6 +824,7 @@ if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil { .package(path: "../swift-argument-parser"), .package(path: "../swift-driver"), .package(path: "../swift-crypto"), + .package(path: "../swift-syntax"), .package(path: "../swift-system"), .package(path: "../swift-collections"), .package(path: "../swift-certificates"), diff --git a/Sources/CMakeLists.txt b/Sources/CMakeLists.txt index 5af06570360..1f4226f88e6 100644 --- a/Sources/CMakeLists.txt +++ b/Sources/CMakeLists.txt @@ -21,6 +21,7 @@ add_subdirectory(PackageFingerprint) add_subdirectory(PackageGraph) add_subdirectory(PackageLoading) add_subdirectory(PackageModel) +add_subdirectory(PackageModelSyntax) add_subdirectory(PackagePlugin) add_subdirectory(PackageRegistry) add_subdirectory(PackageSigning) diff --git a/Sources/Commands/CMakeLists.txt b/Sources/Commands/CMakeLists.txt index e74414c60bf..f7978b6d84e 100644 --- a/Sources/Commands/CMakeLists.txt +++ b/Sources/Commands/CMakeLists.txt @@ -7,6 +7,8 @@ # See http://swift.org/CONTRIBUTORS.txt for Swift project authors add_library(Commands + PackageCommands/AddDependency.swift + PackageCommands/AddTarget.swift PackageCommands/APIDiff.swift PackageCommands/ArchiveSource.swift PackageCommands/CompletionCommand.swift @@ -56,6 +58,7 @@ target_link_libraries(Commands PUBLIC CoreCommands LLBuildManifest PackageGraph + PackageModelSyntax SourceControl TSCBasic TSCUtility diff --git a/Sources/Commands/PackageCommands/AddDependency.swift b/Sources/Commands/PackageCommands/AddDependency.swift new file mode 100644 index 00000000000..f0b21c8b5c7 --- /dev/null +++ b/Sources/Commands/PackageCommands/AddDependency.swift @@ -0,0 +1,155 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import ArgumentParser +import Basics +import CoreCommands +import PackageModel +import PackageModelSyntax +import SwiftParser +import SwiftSyntax +import TSCBasic +import TSCUtility +import Workspace + +extension SwiftPackageCommand { + struct AddDependency: SwiftCommand { + package static let configuration = CommandConfiguration( + abstract: "Add a package dependency to the manifest") + + @Argument(help: "The URL or directory of the package to add") + var dependency: String + + @OptionGroup(visibility: .hidden) + var globalOptions: GlobalOptions + + @Option(help: "The exact package version to depend on") + var exact: Version? + + @Option(help: "The specific package revision to depend on") + var revision: String? + + @Option(help: "The branch of the package to depend on") + var branch: String? + + @Option(help: "The package version to depend on (up to the next major version)") + var from: Version? + + @Option(help: "The package version to depend on (up to the next minor version)") + var upToNextMinorFrom: Version? + + @Option(help: "Specify upper bound on the package version range (exclusive)") + var to: Version? + + func run(_ swiftCommandState: SwiftCommandState) throws { + let workspace = try swiftCommandState.getActiveWorkspace() + + guard let packagePath = try swiftCommandState.getWorkspaceRoot().packages.first else { + throw StringError("unknown package") + } + + // Load the manifest file + let fileSystem = workspace.fileSystem + let manifestPath = packagePath.appending("Package.swift") + let manifestContents: ByteString + do { + manifestContents = try fileSystem.readFileContents(manifestPath) + } catch { + throw StringError("cannot find package manifest in \(manifestPath)") + } + + // Parse the manifest. + let manifestSyntax = manifestContents.withData { data in + data.withUnsafeBytes { buffer in + buffer.withMemoryRebound(to: UInt8.self) { buffer in + Parser.parse(source: buffer) + } + } + } + + let identity = PackageIdentity(url: .init(dependency)) + + // Collect all of the possible version requirements. + var requirements: [PackageDependency.SourceControl.Requirement] = [] + if let exact { + requirements.append(.exact(exact)) + } + + if let branch { + requirements.append(.branch(branch)) + } + + if let revision { + requirements.append(.revision(revision)) + } + + if let from { + requirements.append(.range(.upToNextMajor(from: from))) + } + + if let upToNextMinorFrom { + requirements.append(.range(.upToNextMinor(from: upToNextMinorFrom))) + } + + if requirements.count > 1 { + throw StringError("must specify at most one of --exact, --branch, --revision, --from, or --up-to-next-minor-from") + } + + guard let firstRequirement = requirements.first else { + throw StringError("must specify one of --exact, --branch, --revision, --from, or --up-to-next-minor-from") + } + + let requirement: PackageDependency.SourceControl.Requirement + if case .range(let range) = firstRequirement { + if let to { + requirement = .range(range.lowerBound.. = [ + "targets", + "swiftLanguageVersions", + "cLanguageStandard", + "cxxLanguageStandard" + ] + + /// Produce the set of source edits needed to add the given package + /// dependency to the given manifest file. + public static func addPackageDependency( + _ dependency: PackageDependency, + to manifest: SourceFileSyntax + ) throws -> PackageEditResult { + // Make sure we have a suitable tools version in the manifest. + try manifest.checkEditManifestToolsVersion() + + guard let packageCall = manifest.findCall(calleeName: "Package") else { + throw ManifestEditError.cannotFindPackage + } + + let edits = try packageCall.appendingToArrayArgument( + label: "dependencies", + trailingLabels: Self.argumentLabelsAfterDependencies, + newElement: dependency.asSyntax() + ) + + return PackageEditResult(manifestEdits: edits) + } +} diff --git a/Sources/PackageModelSyntax/AddTarget.swift b/Sources/PackageModelSyntax/AddTarget.swift new file mode 100644 index 00000000000..1157775d985 --- /dev/null +++ b/Sources/PackageModelSyntax/AddTarget.swift @@ -0,0 +1,227 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Basics +import PackageModel +import SwiftParser +import SwiftSyntax +import SwiftSyntaxBuilder + +/// Add a target to a manifest's source code. +public struct AddTarget { + /// The set of argument labels that can occur after the "targets" + /// argument in the Package initializers. + /// + /// TODO: Could we generate this from the the PackageDescription module, so + /// we don't have keep it up-to-date manually? + private static let argumentLabelsAfterTargets: Set = [ + "swiftLanguageVersions", + "cLanguageStandard", + "cxxLanguageStandard" + ] + + /// Add the given target to the manifest, producing a set of edit results + /// that updates the manifest and adds some source files to stub out the + /// new target. + public static func addTarget( + _ target: TargetDescription, + to manifest: SourceFileSyntax + ) throws -> PackageEditResult { + // Make sure we have a suitable tools version in the manifest. + try manifest.checkEditManifestToolsVersion() + + guard let packageCall = manifest.findCall(calleeName: "Package") else { + throw ManifestEditError.cannotFindPackage + } + + // Create a mutable version of target to which we can add more + // content when needed. + var target = target + + // Macro targets need to depend on a couple of libraries from + // SwiftSyntax. + if target.type == .macro { + target.dependencies.append(contentsOf: macroTargetDependencies) + } + + let manifestEdits = try packageCall.appendingToArrayArgument( + label: "targets", + trailingLabels: Self.argumentLabelsAfterTargets, + newElement: target.asSyntax() + ) + + let outerDirectory: String? = switch target.type { + case .binary, .plugin, .system: nil + case .executable, .regular, .macro: "Sources" + case .test: "Tests" + } + + guard let outerDirectory else { + return PackageEditResult(manifestEdits: manifestEdits) + } + + let outerPath = try RelativePath(validating: outerDirectory) + + /// The set of auxiliary files this refactoring will create. + var auxiliaryFiles: AuxiliaryFiles = [] + + // Add the primary source file. Every target type has this. + addPrimarySourceFile( + outerPath: outerPath, + target: target, + to: &auxiliaryFiles + ) + + // Perform any other actions that are needed for this target type. + switch target.type { + case .macro: + // Macros need a file that introduces the main entrypoint + // describing all of the macros. + auxiliaryFiles.addSourceFile( + path: outerPath.appending( + components: [target.name, "ProvidedMacros.swift"] + ), + sourceCode: """ + import SwiftCompilerPlugin + + @main + struct \(raw: target.name)Macros: CompilerPlugin { + let providingMacros: [Macro.Type] = [ + \(raw: target.name).self, + ] + } + """ + ) + + default: break; + } + + return PackageEditResult( + manifestEdits: manifestEdits, + auxiliaryFiles: auxiliaryFiles + ) + } + + /// Add the primary source file for a target to the list of auxiliary + /// source files. + fileprivate static func addPrimarySourceFile( + outerPath: RelativePath, + target: TargetDescription, + to auxiliaryFiles: inout AuxiliaryFiles + ) { + let sourceFilePath = outerPath.appending( + components: [target.name, "\(target.name).swift"] + ) + + // Introduce imports for each of the dependencies that were specified. + var importModuleNames = target.dependencies.map { + $0.name + } + + // Add appropriate test module dependencies. + if target.type == .test { + importModuleNames.append("XCTest") + } + + let importDecls = importModuleNames.lazy.sorted().map { name in + DeclSyntax("import \(raw: name)").with(\.trailingTrivia, .newline) + } + + let imports = CodeBlockItemListSyntax { + for importDecl in importDecls { + importDecl + } + } + + let sourceFileText: SourceFileSyntax = switch target.type { + case .binary, .plugin, .system: + fatalError("should have exited above") + + case .macro: + """ + \(imports) + struct \(raw: target.name): Macro { + /// TODO: Implement one or more of the protocols that inherit + /// from Macro. The appropriate macro protocol is determined + /// by the "macro" declaration that \(raw: target.name) implements. + /// Examples include: + /// @freestanding(expression) macro --> ExpressionMacro + /// @attached(member) macro --> MemberMacro + } + """ + + case .test: + """ + \(imports) + class \(raw: target.name): XCTestCase { + func test\(raw: target.name)() { + XCTAssertEqual(42, 17 + 25) + } + } + """ + + case .regular: + """ + \(imports) + """ + + case .executable: + """ + \(imports) + @main + struct \(raw: target.name)Main { + static func main() { + print("Hello, world") + } + } + """ + } + + auxiliaryFiles.addSourceFile( + path: sourceFilePath, + sourceCode: sourceFileText + ) + } +} + +fileprivate extension TargetDescription.Dependency { + /// Retrieve the name of the dependency + var name: String { + switch self { + case .target(name: let name, condition: _), + .byName(name: let name, condition: _), + .product(name: let name, package: _, moduleAliases: _, condition: _): + name + } + } +} + +/// The array of auxiliary files that can be added by a package editing +/// operation. +fileprivate typealias AuxiliaryFiles = [(RelativePath, SourceFileSyntax)] + +fileprivate extension AuxiliaryFiles { + /// Add a source file to the list of auxiliary files. + mutating func addSourceFile( + path: RelativePath, + sourceCode: SourceFileSyntax + ) { + self.append((path, sourceCode)) + } +} + +/// The set of dependencies we need to introduce to a newly-created macro +/// target. +fileprivate let macroTargetDependencies: [TargetDescription.Dependency] = [ + .product(name: "SwiftCompilerPlugin", package: "swift-syntax"), + .product(name: "SwiftSyntaxMacros", package: "swift-syntax"), +] diff --git a/Sources/PackageModelSyntax/CMakeLists.txt b/Sources/PackageModelSyntax/CMakeLists.txt new file mode 100644 index 00000000000..3a968e84d7e --- /dev/null +++ b/Sources/PackageModelSyntax/CMakeLists.txt @@ -0,0 +1,61 @@ +# This source file is part of the Swift open source project +# +# Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See http://swift.org/LICENSE.txt for license information +# See http://swift.org/CONTRIBUTORS.txt for Swift project authors + +add_library(PackageModelSyntax + AddPackageDependency.swift + AddTarget.swift + ManifestEditError.swift + ManifestSyntaxRepresentable.swift + PackageDependency+Syntax.swift + PackageEditResult.swift + SyntaxEditUtils.swift + TargetDescription+Syntax.swift +) + +target_link_libraries(PackageModelSyntax PUBLIC + Basics + PackageLoading + PackageModel + + SwiftBasicFormat + SwiftDiagnostics + SwiftIDEUtils + SwiftParser + SwiftSyntax + SwiftSyntaxBuilder +) + +# NOTE(compnerd) workaround for CMake not setting up include flags yet +set_target_properties(PackageModelSyntax PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY}) + +install(TARGETS PackageModelSyntax + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin) +set_property(GLOBAL APPEND PROPERTY SwiftPM_EXPORTS PackageModelSyntax) + +set(SWIFT_SYNTAX_MODULES + SwiftBasicFormat + SwiftParser + SwiftParserDiagnostics + SwiftDiagnostics + SwiftSyntax + SwiftOperators + SwiftSyntaxBuilder + SwiftSyntaxMacros + SwiftSyntaxMacroExpansion + SwiftCompilerPluginMessageHandling + # Support for LSP + SwiftIDEUtils + SwiftRefactor +) +export(TARGETS ${SWIFT_SYNTAX_MODULES} + NAMESPACE SwiftSyntax:: + FILE ${CMAKE_BINARY_DIR}/cmake/modules/SwiftSyntaxConfig.cmake + EXPORT_LINK_INTERFACE_LIBRARIES) \ No newline at end of file diff --git a/Sources/PackageModelSyntax/ManifestEditError.swift b/Sources/PackageModelSyntax/ManifestEditError.swift new file mode 100644 index 00000000000..cba8eb520dd --- /dev/null +++ b/Sources/PackageModelSyntax/ManifestEditError.swift @@ -0,0 +1,53 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import PackageLoading +import PackageModel +import SwiftSyntax + +/// An error describing problems that can occur when attempting to edit a +/// package manifest programattically. +package enum ManifestEditError: Error { + case cannotFindPackage + case cannotFindArrayLiteralArgument(argumentName: String, node: Syntax) + case oldManifest(ToolsVersion) +} + +extension ToolsVersion { + /// The minimum tools version of the manifest file that we support edit + /// operations on. + static let minimumManifestEditVersion = v5_5 +} + +extension ManifestEditError: CustomStringConvertible { + package var description: String { + switch self { + case .cannotFindPackage: + "invalid manifest: unable to find 'Package' declaration" + case .cannotFindArrayLiteralArgument(argumentName: let name, node: _): + "unable to find array literal for '\(name)' argument" + case .oldManifest(let version): + "package manifest version \(version) is too old: please update to manifest version \(ToolsVersion.minimumManifestEditVersion) or newer" + } + } +} + +extension SourceFileSyntax { + /// Check that the manifest described by this source file meets the minimum + /// tools version requirements for editing the manifest. + func checkEditManifestToolsVersion() throws { + let toolsVersion = try ToolsVersionParser.parse(utf8String: description) + if toolsVersion < ToolsVersion.minimumManifestEditVersion { + throw ManifestEditError.oldManifest(toolsVersion) + } + } +} diff --git a/Sources/PackageModelSyntax/ManifestSyntaxRepresentable.swift b/Sources/PackageModelSyntax/ManifestSyntaxRepresentable.swift new file mode 100644 index 00000000000..8af1d379c18 --- /dev/null +++ b/Sources/PackageModelSyntax/ManifestSyntaxRepresentable.swift @@ -0,0 +1,46 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Basics +import SwiftSyntax + +/// Describes an entity in the package model that can be represented as +/// a syntax node. +protocol ManifestSyntaxRepresentable { + /// The most specific kind of syntax node that best describes this entity + /// in the manifest. + /// + /// There might be other kinds of syntax nodes that can also represent + /// the syntax, but this is the one that a canonical manifest will use. + /// As an example, a package dependency is usually expressed as, e.g., + /// .package(url: "https://github.com/apple/swift-syntax.git", from: "510.0.1") + /// + /// However, there could be other forms, e.g., this is also valid: + /// Package.Dependency.package(url: "https://github.com/apple/swift-syntax.git", from: "510.0.1") + associatedtype PreferredSyntax: SyntaxProtocol + + /// Provides a suitable syntax node to describe this entity in the package + /// model. + /// + /// The resulting syntax is a fragment that describes just this entity, + /// and it's enclosing entity will need to understand how to fit it in. + /// For example, a `PackageDependency` entity would map to syntax for + /// something like + /// .package(url: "https://github.com/apple/swift-syntax.git", from: "510.0.1") + func asSyntax() -> PreferredSyntax +} + +extension String: ManifestSyntaxRepresentable { + typealias PreferredSyntax = ExprSyntax + + func asSyntax() -> ExprSyntax { "\(literal: self)" } +} diff --git a/Sources/PackageModelSyntax/PackageDependency+Syntax.swift b/Sources/PackageModelSyntax/PackageDependency+Syntax.swift new file mode 100644 index 00000000000..cf870669903 --- /dev/null +++ b/Sources/PackageModelSyntax/PackageDependency+Syntax.swift @@ -0,0 +1,93 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Basics +import PackageModel +import SwiftSyntax +import SwiftParser +import struct TSCUtility.Version + +extension PackageDependency: ManifestSyntaxRepresentable { + func asSyntax() -> ExprSyntax { + switch self { + case .fileSystem(let filesystem): filesystem.asSyntax() + case .sourceControl(let sourceControl): sourceControl.asSyntax() + case .registry(let registry): registry.asSyntax() + } + } +} + +extension PackageDependency.FileSystem: ManifestSyntaxRepresentable { + func asSyntax() -> ExprSyntax { + fatalError() + } +} + +extension PackageDependency.SourceControl: ManifestSyntaxRepresentable { + func asSyntax() -> ExprSyntax { + // TODO: Not handling identity, nameForTargetDependencyResolutionOnly, + // or productFilter yet. + switch location { + case .local(let path): + ".package(path: \(literal: path.description), \(requirement.asSyntax()))" + case .remote(let url): + ".package(url: \(literal: url.description), \(requirement.asSyntax()))" + } + } +} + +extension PackageDependency.Registry: ManifestSyntaxRepresentable { + func asSyntax() -> ExprSyntax { + fatalError() + } +} + +extension PackageDependency.SourceControl.Requirement: ManifestSyntaxRepresentable { + func asSyntax() -> LabeledExprSyntax { + switch self { + case .exact(let version): + LabeledExprSyntax( + label: "exact", + expression: version.asSyntax() + ) + + case .range(let range) where range == .upToNextMajor(from: range.lowerBound): + LabeledExprSyntax( + label: "from", + expression: range.lowerBound.asSyntax() + ) + + case .range(let range): + LabeledExprSyntax( + expression: "\(range.lowerBound.asSyntax())..<\(range.upperBound.asSyntax())" as ExprSyntax + ) + + case .revision(let revision): + LabeledExprSyntax( + label: "revision", + expression: "\(literal: revision)" as ExprSyntax + ) + + case .branch(let branch): + LabeledExprSyntax( + label: "branch", + expression: "\(literal: branch)" as ExprSyntax + ) + } + } +} + +extension Version: ManifestSyntaxRepresentable { + func asSyntax() -> ExprSyntax { + return "\(literal: description)" + } +} diff --git a/Sources/PackageModelSyntax/PackageEditResult.swift b/Sources/PackageModelSyntax/PackageEditResult.swift new file mode 100644 index 00000000000..6de70765eeb --- /dev/null +++ b/Sources/PackageModelSyntax/PackageEditResult.swift @@ -0,0 +1,97 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Basics +@_spi(FixItApplier) import SwiftIDEUtils +import SwiftSyntax + +/// The result of editing a package, including any edits to the package +/// manifest and any new files that are introduced. +public struct PackageEditResult { + /// Edits to perform to the package manifest. + public var manifestEdits: [SourceEdit] = [] + + /// Auxiliary files to write. + public var auxiliaryFiles: [(RelativePath, SourceFileSyntax)] = [] +} + +extension PackageEditResult { + /// Apply the edits for the given manifest to the specified file system, + /// updating the manifest to the given manifest + public func applyEdits( + to filesystem: any FileSystem, + manifest: SourceFileSyntax, + manifestPath: AbsolutePath, + verbose: Bool + ) throws { + let rootPath = manifestPath.parentDirectory + + // Update the manifest + if verbose { + print("Updating package manifest at \(manifestPath.relative(to: rootPath))...", terminator: "") + } + + let updatedManifestSource = FixItApplier.apply( + edits: manifestEdits, + to: manifest + ) + try filesystem.writeFileContents( + manifestPath, + string: updatedManifestSource + ) + if verbose { + print(" done.") + } + + // Write all of the auxiliary files. + for (auxiliaryFileRelPath, auxiliaryFileSyntax) in auxiliaryFiles { + // If the file already exists, skip it. + let filePath = rootPath.appending(auxiliaryFileRelPath) + if filesystem.exists(filePath) { + if verbose { + print("Skipping \(filePath.relative(to: rootPath)) because it already exists.") + } + + continue + } + + // If the directory does not exist yet, create it. + let fileDir = filePath.parentDirectory + if !filesystem.exists(fileDir) { + if verbose { + print("Creating directory \(fileDir.relative(to: rootPath))...", terminator: "") + } + + try filesystem.createDirectory(fileDir, recursive: true) + + if verbose { + print(" done.") + } + } + + // Write the file. + if verbose { + print("Writing \(filePath.relative(to: rootPath))...", terminator: "") + } + + try filesystem.writeFileContents( + filePath, + string: auxiliaryFileSyntax.description + ) + + if verbose { + print(" done.") + } + } + } + +} diff --git a/Sources/PackageModelSyntax/SyntaxEditUtils.swift b/Sources/PackageModelSyntax/SyntaxEditUtils.swift new file mode 100644 index 00000000000..774853a46f7 --- /dev/null +++ b/Sources/PackageModelSyntax/SyntaxEditUtils.swift @@ -0,0 +1,462 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Basics +import PackageModel +import SwiftBasicFormat +import SwiftSyntax +import SwiftParser + +/// Default indent when we have to introduce indentation but have no context +/// to get it right. +let defaultIndent = TriviaPiece.spaces(4) + +extension Trivia { + /// Determine whether this trivia has newlines or not. + var hasNewlines: Bool { + contains(where: \.isNewline) + } +} + +/// Syntax walker to find the first occurrence of a given node kind that +/// matches a specific predicate. +private class FirstNodeFinder: SyntaxAnyVisitor { + var predicate: (Node) -> Bool + var found: Node? = nil + + init(predicate: @escaping (Node) -> Bool) { + self.predicate = predicate + super.init(viewMode: .sourceAccurate) + } + + override func visitAny(_ node: Syntax) -> SyntaxVisitorContinueKind { + if found != nil { + return .skipChildren + } + + if let matchedNode = node.as(Node.self), predicate(matchedNode) { + found = matchedNode + return .skipChildren + } + + return .visitChildren + } +} + +extension SyntaxProtocol { + /// Find the first node of the Self type that matches the given predicate. + static func findFirst( + in node: some SyntaxProtocol, + matching predicate: (Self) -> Bool + ) -> Self? { + withoutActuallyEscaping(predicate) { escapingPredicate in + let visitor = FirstNodeFinder(predicate: escapingPredicate) + visitor.walk(node) + return visitor.found + } + } +} + +extension FunctionCallExprSyntax { + /// Check whether this call expression has a callee that is a reference + /// to a declaration with the given name. + func hasCallee(named name: String) -> Bool { + guard let calleeDeclRef = calledExpression.as(DeclReferenceExprSyntax.self) else { + return false + } + + return calleeDeclRef.baseName.text == name + } + + /// Find a call argument based on its label. + func findArgument(labeled label: String) -> LabeledExprSyntax? { + arguments.first { $0.label?.text == label } + } +} + +extension LabeledExprListSyntax { + /// Find the index at which the one would insert a new argument given + /// the set of argument labels that could come after the argument we + /// want to insert. + func findArgumentInsertionPosition( + labelsAfter: Set + ) -> SyntaxChildrenIndex { + firstIndex { + guard let label = $0.label else { + return false + } + + return labelsAfter.contains(label.text) + } ?? endIndex + } + + /// Form a new argument list that inserts a new argument at the specified + /// position in this argument list. + /// + /// This operation will attempt to introduce trivia to match the + /// surrounding context where possible. The actual argument will be + /// created by the `generator` function, which is provided with leading + /// trivia and trailing comma it should use to match the surrounding + /// context. + func insertingArgument( + at position: SyntaxChildrenIndex, + generator: (Trivia, TokenSyntax?) -> LabeledExprSyntax + ) -> LabeledExprListSyntax { + // Turn the arguments into an array so we can manipulate them. + var arguments = Array(self) + + let positionIdx = distance(from: startIndex, to: position) + + let commaToken = TokenSyntax.commaToken() + + // Figure out leading trivia and adjust the prior argument (if there is + // one) by adding a comma, if necessary. + let leadingTrivia: Trivia + if position > startIndex { + let priorArgument = arguments[positionIdx - 1] + + // Our leading trivia will be based on the prior argument's leading + // trivia. + leadingTrivia = priorArgument.leadingTrivia + + // If the prior argument is missing a trailing comma, add one. + if priorArgument.trailingComma == nil { + arguments[positionIdx - 1].trailingComma = commaToken + } + } else if positionIdx + 1 < count { + leadingTrivia = arguments[positionIdx + 1].leadingTrivia + } else { + leadingTrivia = Trivia() + } + + // Determine whether we need a trailing comma on this argument. + let trailingComma: TokenSyntax? + if position < endIndex { + trailingComma = commaToken + } else { + trailingComma = nil + } + + // Create the argument and insert it into the argument list. + let argument = generator(leadingTrivia, trailingComma) + arguments.insert(argument, at: positionIdx) + + return LabeledExprListSyntax(arguments) + } +} + +extension SyntaxProtocol { + /// Look for a call expression to a callee with the given name. + func findCall(calleeName: String) -> FunctionCallExprSyntax? { + return FunctionCallExprSyntax.findFirst(in: self) { call in + return call.hasCallee(named: calleeName) + } + } +} + +extension ArrayExprSyntax { + /// Produce a new array literal expression that appends the given + /// element, while trying to maintain similar indentation. + func appending( + element: ExprSyntax, + outerLeadingTrivia: Trivia + ) -> ArrayExprSyntax { + var elements = self.elements + + let commaToken = TokenSyntax.commaToken() + + // If there are already elements, tack it on. + let leadingTrivia: Trivia + let trailingTrivia: Trivia + let leftSquareTrailingTrivia: Trivia + if let last = elements.last { + // The leading trivia of the new element should match that of the + // last element. + leadingTrivia = last.leadingTrivia + + // Add a trailing comma to the last element if it isn't already + // there. + if last.trailingComma == nil { + var newElements = Array(elements) + newElements[newElements.count - 1].trailingComma = commaToken + newElements[newElements.count - 1].expression.trailingTrivia = + Trivia() + newElements[newElements.count - 1].trailingTrivia = last.trailingTrivia + elements = ArrayElementListSyntax(newElements) + } + + trailingTrivia = Trivia() + leftSquareTrailingTrivia = leftSquare.trailingTrivia + } else { + leadingTrivia = outerLeadingTrivia.appending(defaultIndent) + trailingTrivia = outerLeadingTrivia + if leftSquare.trailingTrivia.hasNewlines { + leftSquareTrailingTrivia = leftSquare.trailingTrivia + } else { + leftSquareTrailingTrivia = Trivia() + } + } + + elements.append( + ArrayElementSyntax( + expression: element.with(\.leadingTrivia, leadingTrivia), + trailingComma: commaToken.with(\.trailingTrivia, trailingTrivia) + ) + ) + + let newLeftSquare = leftSquare.with( + \.trailingTrivia, + leftSquareTrailingTrivia + ) + + return with(\.elements, elements).with(\.leftSquare, newLeftSquare) + } +} + +extension ExprSyntax { + /// Find an array argument either at the top level or within a sequence + /// expression. + func findArrayArgument() -> ArrayExprSyntax? { + if let arrayExpr = self.as(ArrayExprSyntax.self) { + return arrayExpr + } + + if let sequenceExpr = self.as(SequenceExprSyntax.self) { + return sequenceExpr.elements.lazy.compactMap { + $0.findArrayArgument() + }.first + } + + return nil + } +} + +// MARK: Utilities to oeprate on arrays of array literal elements. +extension Array { + /// Append a new argument expression. + mutating func append(expression: ExprSyntax) { + // Add a comma on the prior expression, if there is one. + let leadingTrivia: Trivia? + if count > 0 { + self[count - 1].trailingComma = TokenSyntax.commaToken() + leadingTrivia = .newline + + // Adjust the first element to start with a newline + if count == 1 { + self[0].leadingTrivia = .newline + } + } else { + leadingTrivia = nil + } + + append( + ArrayElementSyntax( + leadingTrivia: leadingTrivia, + expression: expression + ) + ) + } +} + +// MARK: Utilities to operate on arrays of call arguments. + +extension Array { + /// Append a potentially labeled argument with the argument expression. + mutating func append(label: String?, expression: ExprSyntax) { + // Add a comma on the prior expression, if there is one. + let leadingTrivia: Trivia + if count > 0 { + self[count - 1].trailingComma = TokenSyntax.commaToken() + leadingTrivia = .newline + + // Adjust the first element to start with a newline + if count == 1 { + self[0].leadingTrivia = .newline + } + } else { + leadingTrivia = Trivia() + } + + // Add the new expression. + append( + LabeledExprSyntax( + label: label, + expression: expression + ).with(\.leadingTrivia, leadingTrivia) + ) + } + + /// Append a potentially labeled argument with a string literal. + mutating func append(label: String?, stringLiteral: String) { + append(label: label, expression: "\(literal: stringLiteral)") + } + + /// Append a potentially labeled argument with a string literal, but only + /// when the string literal is not nil. + mutating func appendIf(label: String?, stringLiteral: String?) { + if let stringLiteral { + append(label: label, stringLiteral: stringLiteral) + } + } + + /// Append an array literal containing elements that can be rendered + /// into expression syntax nodes. + mutating func append( + label: String?, + arrayLiteral: [T] + ) where T: ManifestSyntaxRepresentable, T.PreferredSyntax == ExprSyntax { + var elements: [ArrayElementSyntax] = [] + for element in arrayLiteral { + elements.append(expression: element.asSyntax()) + } + + // When we have more than one element in the array literal, we add + // newlines at the beginning of each element. Do the same for the + // right square bracket. + let rightSquareLeadingTrivia: Trivia = elements.count > 0 + ? .newline + : Trivia() + + let array = ArrayExprSyntax( + elements: ArrayElementListSyntax(elements), + rightSquare: .rightSquareToken( + leadingTrivia: rightSquareLeadingTrivia + ) + ) + append(label: label, expression: ExprSyntax(array)) + } + + /// Append an array literal containing elements that can be rendered + /// into expression syntax nodes. + mutating func appendIf( + label: String?, + arrayLiteral: [T]? + ) where T: ManifestSyntaxRepresentable, T.PreferredSyntax == ExprSyntax { + guard let arrayLiteral else { return } + append(label: label, arrayLiteral: arrayLiteral) + } + + /// Append an array literal containing elements that can be rendered + /// into expression syntax nodes, but only if it's not empty. + mutating func appendIfNonEmpty( + label: String?, + arrayLiteral: [T] + ) where T: ManifestSyntaxRepresentable, T.PreferredSyntax == ExprSyntax { + if arrayLiteral.isEmpty { return } + + append(label: label, arrayLiteral: arrayLiteral) + } +} + +// MARK: Utilities for adding arguments into calls. +extension FunctionCallExprSyntax { + /// Produce source edits that will add the given new element to the + /// array for an argument with the given label (if there is one), or + /// introduce a new argument with an array literal containing only the + /// new element. + /// + /// - Parameters: + /// - label: The argument label for the argument whose array will be + /// added or modified. + /// - trailingLabels: The argument labels that could follow the label, + /// which helps determine where the argument should be inserted if + /// it doesn't exist yet. + /// - newElement: The new element. + /// - Returns: the resulting source edits to make this change. + func appendingToArrayArgument( + label: String, + trailingLabels: Set, + newElement: ExprSyntax + ) throws -> [SourceEdit] { + // If there is already an argument with this name, append to the array + // literal in there. + if let arg = findArgument(labeled: label) { + guard let argArray = arg.expression.findArrayArgument() else { + throw ManifestEditError.cannotFindArrayLiteralArgument( + argumentName: label, + node: Syntax(arg.expression) + ) + } + + // Format the element appropriately for the context. + let indentation = Trivia( + pieces: arg.leadingTrivia.filter { $0.isSpaceOrTab } + ) + let format = BasicFormat( + indentationWidth: [ defaultIndent ], + initialIndentation: indentation.appending(defaultIndent) + ) + let formattedElement = newElement.formatted(using: format) + .cast(ExprSyntax.self) + + let updatedArgArray = argArray.appending( + element: formattedElement, + outerLeadingTrivia: arg.leadingTrivia + ) + return [ .replace(argArray, with: updatedArgArray.description) ] + } + + // There was no argument, so we need to create one. + + // Insert the new argument at the appropriate place in the call. + let insertionPos = arguments.findArgumentInsertionPosition( + labelsAfter: trailingLabels + ) + let newArguments = arguments.insertingArgument( + at: insertionPos + ) { (leadingTrivia, trailingComma) in + // Format the element appropriately for the context. + let indentation = Trivia(pieces: leadingTrivia.filter { $0.isSpaceOrTab }) + let format = BasicFormat( + indentationWidth: [ defaultIndent ], + initialIndentation: indentation.appending(defaultIndent) + ) + let formattedElement = newElement.formatted(using: format) + .cast(ExprSyntax.self) + + // Form the array. + let newArgument = ArrayExprSyntax( + leadingTrivia: .space, + leftSquare: .leftSquareToken( + trailingTrivia: .newline + ), + elements: ArrayElementListSyntax( + [ + ArrayElementSyntax( + expression: formattedElement, + trailingComma: .commaToken() + ) + ] + ), + rightSquare: .rightSquareToken( + leadingTrivia: leadingTrivia + ) + ) + + // Create the labeled argument for the array. + return LabeledExprSyntax( + leadingTrivia: leadingTrivia, + label: "\(raw: label)", + colon: .colonToken(), + expression: ExprSyntax(newArgument), + trailingComma: trailingComma + ) + } + + return [ + SourceEdit.replace( + arguments, + with: newArguments.description + ) + ] + } +} diff --git a/Sources/PackageModelSyntax/TargetDescription+Syntax.swift b/Sources/PackageModelSyntax/TargetDescription+Syntax.swift new file mode 100644 index 00000000000..f47f6590f06 --- /dev/null +++ b/Sources/PackageModelSyntax/TargetDescription+Syntax.swift @@ -0,0 +1,99 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Basics +import PackageModel +import SwiftSyntax +import SwiftParser + + +extension TargetDescription: ManifestSyntaxRepresentable { + /// The function name in the package manifest. + private var functionName: String { + switch type { + case .binary: "binaryTarget" + case .executable: "executableTarget" + case .macro: "macro" + case .plugin: "plugin" + case .regular: "target" + case .system: "systemLibrary" + case .test: "testTarget" + } + } + + func asSyntax() -> ExprSyntax { + var arguments: [LabeledExprSyntax] = [] + arguments.append(label: "name", stringLiteral: name) + // FIXME: pluginCapability + + arguments.appendIfNonEmpty( + label: "dependencies", + arrayLiteral: dependencies + ) + + arguments.appendIf(label: "path", stringLiteral: path) + arguments.appendIf(label: "url", stringLiteral: url) + arguments.appendIfNonEmpty(label: "exclude", arrayLiteral: exclude) + arguments.appendIf(label: "sources", arrayLiteral: sources) + + // FIXME: resources + + arguments.appendIf( + label: "publicHeadersPath", + stringLiteral: publicHeadersPath + ) + + if !packageAccess { + arguments.append( + label: "packageAccess", + expression: "false" + ) + } + + // FIXME: cSettings + // FIXME: cxxSettings + // FIXME: swiftSettings + // FIXME: linkerSettings + // FIXME: plugins + + arguments.appendIf(label: "pkgConfig", stringLiteral: pkgConfig) + // FIXME: providers + + // Only for plugins + arguments.appendIf(label: "checksum", stringLiteral: checksum) + + let separateParen: String = arguments.count > 1 ? "\n" : "" + let argumentsSyntax = LabeledExprListSyntax(arguments) + return ".\(raw: functionName)(\(argumentsSyntax)\(raw: separateParen))" + } +} + +extension TargetDescription.Dependency: ManifestSyntaxRepresentable { + func asSyntax() -> ExprSyntax { + switch self { + case .byName(name: let name, condition: nil): + "\(literal: name)" + + case .target(name: let name, condition: nil): + ".target(name: \(literal: name))" + + case .product(name: let name, package: nil, moduleAliases: nil, condition: nil): + ".product(name: \(literal: name))" + + case .product(name: let name, package: let package, moduleAliases: nil, condition: nil): + ".product(name: \(literal: name), package: \(literal: package))" + + default: + fatalError() + } + } +} diff --git a/Sources/Workspace/Workspace.swift b/Sources/Workspace/Workspace.swift index 38af1b7362b..e6016e3d554 100644 --- a/Sources/Workspace/Workspace.swift +++ b/Sources/Workspace/Workspace.swift @@ -96,7 +96,7 @@ public class Workspace { public let pinsStore: LoadableResult /// The file system on which the workspace will operate. - let fileSystem: any FileSystem + package let fileSystem: any FileSystem /// The host toolchain to use. private let hostToolchain: UserToolchain diff --git a/Tests/CommandsTests/PackageCommandTests.swift b/Tests/CommandsTests/PackageCommandTests.swift index d34e4c898fa..82c4810ef1d 100644 --- a/Tests/CommandsTests/PackageCommandTests.swift +++ b/Tests/CommandsTests/PackageCommandTests.swift @@ -793,6 +793,64 @@ final class PackageCommandTests: CommandsTestCase { } } + func testPackageAddDependency() throws { + try testWithTemporaryDirectory { tmpPath in + let fs = localFileSystem + let path = tmpPath.appending("PackageB") + try fs.createDirectory(path) + + try fs.writeFileContents(path.appending("Package.swift"), string: + """ + // swift-tools-version: 5.9 + import PackageDescription + let package = Package( + name: "client", + targets: [ .target(name: "client", dependencies: [ "library" ]) ] + ) + """ + ) + + _ = try execute(["add-dependency", "--branch", "main", "https://github.com/apple/swift-syntax.git"], packagePath: path) + + let manifest = path.appending("Package.swift") + XCTAssertFileExists(manifest) + let contents: String = try fs.readFileContents(manifest) + + XCTAssertMatch(contents, .contains(#".package(url: "https://github.com/apple/swift-syntax.git", branch: "main"),"#)) + } + } + + func testPackageAddTarget() throws { + try testWithTemporaryDirectory { tmpPath in + let fs = localFileSystem + let path = tmpPath.appending("PackageB") + try fs.createDirectory(path) + + try fs.writeFileContents(path.appending("Package.swift"), string: + """ + // swift-tools-version: 5.9 + import PackageDescription + let package = Package( + name: "client" + ) + """ + ) + + _ = try execute(["add-target", "client", "--dependencies", "MyLib", "OtherLib", "--type", "executable"], packagePath: path) + + let manifest = path.appending("Package.swift") + XCTAssertFileExists(manifest) + let contents: String = try fs.readFileContents(manifest) + + XCTAssertMatch(contents, .contains(#"targets:"#)) + XCTAssertMatch(contents, .contains(#".executableTarget"#)) + XCTAssertMatch(contents, .contains(#"name: "client""#)) + XCTAssertMatch(contents, .contains(#"dependencies:"#)) + XCTAssertMatch(contents, .contains(#""MyLib""#)) + XCTAssertMatch(contents, .contains(#""OtherLib""#)) + } + } + func testPackageEditAndUnedit() throws { try fixture(name: "Miscellaneous/PackageEdit") { fixturePath in let fooPath = fixturePath.appending("foo") @@ -1926,13 +1984,13 @@ final class PackageCommandTests: CommandsTestCase { try fixture(name: "Miscellaneous/Plugins/CommandPluginTestStub") { fixturePath in func runPlugin(flags: [String], diagnostics: [String], completion: (String, String) -> Void) throws { - let (stdout, stderr) = try SwiftPM.Package.execute(flags + ["print-diagnostics"] + diagnostics, packagePath: fixturePath) + let (stdout, stderr) = try SwiftPM.Package.execute(flags + ["print-diagnostics"] + diagnostics, packagePath: fixturePath, env: ["SWIFT_DRIVER_SWIFTSCAN_LIB" : "/this/is/a/bad/path"]) completion(stdout, stderr) } // Diagnostics.error causes SwiftPM to return a non-zero exit code, but we still need to check stdout and stderr func runPluginWithError(flags: [String], diagnostics: [String], completion: (String, String) -> Void) throws { - XCTAssertThrowsError(try SwiftPM.Package.execute(flags + ["print-diagnostics"] + diagnostics, packagePath: fixturePath)) { error in + XCTAssertThrowsError(try SwiftPM.Package.execute(flags + ["print-diagnostics"] + diagnostics, packagePath: fixturePath, env: ["SWIFT_DRIVER_SWIFTSCAN_LIB" : "/this/is/a/bad/path"])) { error in guard case SwiftPMError.executionFailure(_, let stdout, let stderr) = error else { return XCTFail("invalid error \(error)") } @@ -2124,28 +2182,28 @@ final class PackageCommandTests: CommandsTestCase { // Check than nothing is echoed when echoLogs is false try fixture(name: "Miscellaneous/Plugins/CommandPluginTestStub") { fixturePath in - let (stdout, stderr) = try SwiftPM.Package.execute(["print-diagnostics", "build"], packagePath: fixturePath) + let (stdout, stderr) = try SwiftPM.Package.execute(["print-diagnostics", "build"], packagePath: fixturePath, env: ["SWIFT_DRIVER_SWIFTSCAN_LIB" : "/this/is/a/bad/path"]) XCTAssertMatch(stdout, isEmpty) XCTAssertMatch(stderr, isEmpty) } // Check that logs are returned to the plugin when echoLogs is false try fixture(name: "Miscellaneous/Plugins/CommandPluginTestStub") { fixturePath in - let (stdout, stderr) = try SwiftPM.Package.execute(["print-diagnostics", "build", "printlogs"], packagePath: fixturePath) + let (stdout, stderr) = try SwiftPM.Package.execute(["print-diagnostics", "build", "printlogs"], packagePath: fixturePath, env: ["SWIFT_DRIVER_SWIFTSCAN_LIB" : "/this/is/a/bad/path"]) XCTAssertMatch(stdout, containsLogtext) XCTAssertMatch(stderr, isEmpty) } // Check that logs echoed to the console (on stderr) when echoLogs is true try fixture(name: "Miscellaneous/Plugins/CommandPluginTestStub") { fixturePath in - let (stdout, stderr) = try SwiftPM.Package.execute(["print-diagnostics", "build", "echologs"], packagePath: fixturePath) + let (stdout, stderr) = try SwiftPM.Package.execute(["print-diagnostics", "build", "echologs"], packagePath: fixturePath, env: ["SWIFT_DRIVER_SWIFTSCAN_LIB" : "/this/is/a/bad/path"]) XCTAssertMatch(stdout, isEmpty) XCTAssertMatch(stderr, containsLogecho) } // Check that logs are returned to the plugin and echoed to the console (on stderr) when echoLogs is true try fixture(name: "Miscellaneous/Plugins/CommandPluginTestStub") { fixturePath in - let (stdout, stderr) = try SwiftPM.Package.execute(["print-diagnostics", "build", "printlogs", "echologs"], packagePath: fixturePath) + let (stdout, stderr) = try SwiftPM.Package.execute(["print-diagnostics", "build", "printlogs", "echologs"], packagePath: fixturePath, env: ["SWIFT_DRIVER_SWIFTSCAN_LIB" : "/this/is/a/bad/path"]) XCTAssertMatch(stdout, containsLogtext) XCTAssertMatch(stderr, containsLogecho) } @@ -2438,14 +2496,14 @@ final class PackageCommandTests: CommandsTestCase { // Check arguments do { - let (stdout, stderr) = try SwiftPM.Package.execute(["plugin", "MyPlugin", "--foo", "--help", "--version", "--verbose"], packagePath: packageDir) + let (stdout, stderr) = try SwiftPM.Package.execute(["plugin", "MyPlugin", "--foo", "--help", "--version", "--verbose"], packagePath: packageDir, env: ["SWIFT_DRIVER_SWIFTSCAN_LIB" : "/this/is/a/bad/path"]) XCTAssertMatch(stdout, .contains("success")) XCTAssertEqual(stderr, "") } // Check default command arguments do { - let (stdout, stderr) = try SwiftPM.Package.execute(["MyPlugin", "--foo", "--help", "--version", "--verbose"], packagePath: packageDir) + let (stdout, stderr) = try SwiftPM.Package.execute(["MyPlugin", "--foo", "--help", "--version", "--verbose"], packagePath: packageDir, env: ["SWIFT_DRIVER_SWIFTSCAN_LIB" : "/this/is/a/bad/path"]) XCTAssertMatch(stdout, .contains("success")) XCTAssertEqual(stderr, "") } diff --git a/Tests/FunctionalTests/MiscellaneousTests.swift b/Tests/FunctionalTests/MiscellaneousTests.swift index b5e5dc3e28f..badae071951 100644 --- a/Tests/FunctionalTests/MiscellaneousTests.swift +++ b/Tests/FunctionalTests/MiscellaneousTests.swift @@ -665,7 +665,7 @@ class MiscellaneousTestCase: XCTestCase { func testRootPackageWithConditionals() throws { try fixture(name: "Miscellaneous/RootPackageWithConditionals") { path in - let (_, stderr) = try SwiftPM.Build.execute(packagePath: path) + let (_, stderr) = try SwiftPM.Build.execute(packagePath: path, env: ["SWIFT_DRIVER_SWIFTSCAN_LIB" : "/this/is/a/bad/path"]) let errors = stderr.components(separatedBy: .newlines).filter { !$0.contains("[logging] misuse") && !$0.isEmpty } XCTAssertEqual(errors, [], "unexpected errors: \(errors)") } diff --git a/Tests/FunctionalTests/PluginTests.swift b/Tests/FunctionalTests/PluginTests.swift index c9bb4794df6..47218b62df3 100644 --- a/Tests/FunctionalTests/PluginTests.swift +++ b/Tests/FunctionalTests/PluginTests.swift @@ -227,12 +227,12 @@ final class PluginTests: XCTestCase { let pathOfGeneratedFile = packageDir.appending(components: [".build", "plugins", "outputs", "mypackage", "SomeTarget", "Plugin", "best.txt"]) try createPackageUnderTest(packageDir: packageDir, toolsVersion: .v5_9) - let (_, stderr) = try executeSwiftBuild(packageDir) + let (_, stderr) = try executeSwiftBuild(packageDir, env: ["SWIFT_DRIVER_SWIFTSCAN_LIB" : "/this/is/a/bad/path"]) XCTAssertTrue(stderr.contains("warning: Build tool command 'empty' (applied to target 'SomeTarget') does not declare any output files"), "expected warning not emitted") XCTAssertFalse(localFileSystem.exists(pathOfGeneratedFile), "plugin generated file unexpectedly exists at \(pathOfGeneratedFile.pathString)") try createPackageUnderTest(packageDir: packageDir, toolsVersion: .v6_0) - let (_, stderr2) = try executeSwiftBuild(packageDir) + let (_, stderr2) = try executeSwiftBuild(packageDir, env: ["SWIFT_DRIVER_SWIFTSCAN_LIB" : "/this/is/a/bad/path"]) XCTAssertEqual("", stderr2) XCTAssertTrue(localFileSystem.exists(pathOfGeneratedFile), "plugin did not run, generated file does not exist at \(pathOfGeneratedFile.pathString)") } diff --git a/Tests/FunctionalTests/ResourcesTests.swift b/Tests/FunctionalTests/ResourcesTests.swift index 85c10611032..95e6a192bb4 100644 --- a/Tests/FunctionalTests/ResourcesTests.swift +++ b/Tests/FunctionalTests/ResourcesTests.swift @@ -157,7 +157,7 @@ class ResourcesTests: XCTestCase { try localFileSystem.createDirectory(resource.parentDirectory, recursive: true) try localFileSystem.writeFileContents(resource, string: "best") - let (_, stderr) = try executeSwiftBuild(packageDir) + let (_, stderr) = try executeSwiftBuild(packageDir, env: ["SWIFT_DRIVER_SWIFTSCAN_LIB" : "/this/is/a/bad/path"]) // Filter some unrelated output that could show up on stderr. let filteredStderr = stderr.components(separatedBy: "\n").filter { !$0.contains("[logging]") }.joined(separator: "\n") XCTAssertEqual(filteredStderr, "", "unexpectedly received error output: \(stderr)") diff --git a/Tests/PackageModelSyntaxTests/ManifestEditTests.swift b/Tests/PackageModelSyntaxTests/ManifestEditTests.swift new file mode 100644 index 00000000000..c61a6036007 --- /dev/null +++ b/Tests/PackageModelSyntaxTests/ManifestEditTests.swift @@ -0,0 +1,666 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +import Basics +import PackageModel +import PackageModelSyntax +import SPMTestSupport +@_spi(FixItApplier) import SwiftIDEUtils +import SwiftParser +import SwiftSyntax +import struct TSCUtility.Version +import XCTest + +/// Assert that applying the given edit/refactor operation to the manifest +/// produces the expected manifest source file and the expected auxiliary +/// files. +func assertManifestRefactor( + _ originalManifest: SourceFileSyntax, + expectedManifest: SourceFileSyntax, + expectedAuxiliarySources: [RelativePath: SourceFileSyntax] = [:], + file: StaticString = #filePath, + line: UInt = #line, + operation: (SourceFileSyntax) throws -> PackageEditResult +) rethrows { + let edits = try operation(originalManifest) + let editedManifestSource = FixItApplier.apply( + edits: edits.manifestEdits, + to: originalManifest + ) + + let editedManifest = Parser.parse(source: editedManifestSource) + assertStringsEqualWithDiff( + editedManifest.description, + expectedManifest.description, + file: file, + line: line + ) + + // Check all of the auxiliary sources. + for (auxSourcePath, auxSourceSyntax) in edits.auxiliaryFiles { + guard let expectedSyntax = expectedAuxiliarySources[auxSourcePath] else { + XCTFail("unexpected auxiliary source file \(auxSourcePath)") + return + } + + assertStringsEqualWithDiff( + auxSourceSyntax.description, + expectedSyntax.description, + file: file, + line: line + ) + } + + XCTAssertEqual( + edits.auxiliaryFiles.count, + expectedAuxiliarySources.count, + "didn't get all of the auxiliary files we expected" + ) +} + +class ManifestEditTests: XCTestCase { + static let swiftSystemURL: SourceControlURL = "https://github.com/apple/swift-system.git" + static let swiftSystemPackageDependency = PackageDependency.remoteSourceControl( + identity: PackageIdentity(url: swiftSystemURL), + nameForTargetDependencyResolutionOnly: nil, + url: swiftSystemURL, + requirement: .branch("main"), productFilter: .nothing + ) + + func testAddPackageDependencyExistingComma() throws { + try assertManifestRefactor(""" + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: [ + .package(url: "https://github.com/apple/swift-syntax.git", from: "510.0.1"), + ] + ) + """, expectedManifest: """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: [ + .package(url: "https://github.com/apple/swift-syntax.git", from: "510.0.1"), + .package(url: "https://github.com/apple/swift-system.git", branch: "main"), + ] + ) + """) { manifest in + try AddPackageDependency.addPackageDependency( + PackageDependency.remoteSourceControl( + identity: PackageIdentity(url: Self.swiftSystemURL), + nameForTargetDependencyResolutionOnly: nil, + url: Self.swiftSystemURL, + requirement: .branch("main"), productFilter: .nothing + ), + to: manifest + ) + } + } + + func testAddPackageDependencyExistingNoComma() throws { + try assertManifestRefactor(""" + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: [ + .package(url: "https://github.com/apple/swift-syntax.git", from: "510.0.1") + ] + ) + """, expectedManifest: """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: [ + .package(url: "https://github.com/apple/swift-syntax.git", from: "510.0.1"), + .package(url: "https://github.com/apple/swift-system.git", exact: "510.0.0"), + ] + ) + """) { manifest in + try AddPackageDependency.addPackageDependency( + PackageDependency.remoteSourceControl( + identity: PackageIdentity(url: Self.swiftSystemURL), + nameForTargetDependencyResolutionOnly: nil, + url: Self.swiftSystemURL, + requirement: .exact("510.0.0"), + productFilter: .nothing + ), + to: manifest + ) + } + } + + func testAddPackageDependencyExistingAppended() throws { + try assertManifestRefactor(""" + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: [ + .package(url: "https://github.com/apple/swift-syntax.git", from: "510.0.1") + ] + [] + ) + """, expectedManifest: """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: [ + .package(url: "https://github.com/apple/swift-syntax.git", from: "510.0.1"), + .package(url: "https://github.com/apple/swift-system.git", from: "510.0.0"), + ] + [] + ) + """) { manifest in + let versionRange = Range.upToNextMajor(from: Version(510, 0, 0)) + + return try AddPackageDependency.addPackageDependency( + PackageDependency.remoteSourceControl( + identity: PackageIdentity(url: Self.swiftSystemURL), + nameForTargetDependencyResolutionOnly: nil, + url: Self.swiftSystemURL, + requirement: .range(versionRange), + productFilter: .nothing + ), + to: manifest + ) + } + } + + func testAddPackageDependencyExistingOneLine() throws { + try assertManifestRefactor(""" + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: [ .package(url: "https://github.com/apple/swift-syntax.git", from: "510.0.1") ] + ) + """, expectedManifest: """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: [ .package(url: "https://github.com/apple/swift-syntax.git", from: "510.0.1"), .package(url: "https://github.com/apple/swift-system.git", from: "510.0.0"),] + ) + """) { manifest in + let versionRange = Range.upToNextMajor(from: Version(510, 0, 0)) + + return try AddPackageDependency.addPackageDependency( + PackageDependency.remoteSourceControl( + identity: PackageIdentity(url: Self.swiftSystemURL), + nameForTargetDependencyResolutionOnly: nil, + url: Self.swiftSystemURL, + requirement: .range(versionRange), + productFilter: .nothing + ), + to: manifest + ) + } + } + func testAddPackageDependencyExistingEmpty() throws { + try assertManifestRefactor(""" + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: [ ] + ) + """, + expectedManifest: """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: [ + .package(url: "https://github.com/apple/swift-system.git", "508.0.0" ..< "510.0.0"), + ] + ) + """) { manifest in + try AddPackageDependency.addPackageDependency( + PackageDependency.remoteSourceControl( + identity: PackageIdentity(url: Self.swiftSystemURL), + nameForTargetDependencyResolutionOnly: nil, + url: Self.swiftSystemURL, + requirement: .range(Version(508,0,0).. ExpressionMacro + /// @attached(member) macro --> MemberMacro + } + """, + RelativePath("Sources/MyMacro/ProvidedMacros.swift") : """ + import SwiftCompilerPlugin + + @main + struct MyMacroMacros: CompilerPlugin { + let providingMacros: [Macro.Type] = [ + MyMacro.self, + ] + } + """ + ] + ) { manifest in + try AddTarget.addTarget( + TargetDescription(name: "MyMacro", type: .macro), + to: manifest + ) + } + } + +} + + +// FIXME: Copy-paste from _SwiftSyntaxTestSupport + +/// Asserts that the two strings are equal, providing Unix `diff`-style output if they are not. +/// +/// - Parameters: +/// - actual: The actual string. +/// - expected: The expected string. +/// - message: An optional description of the failure. +/// - additionalInfo: Additional information about the failed test case that will be printed after the diff +/// - file: The file in which failure occurred. Defaults to the file name of the test case in +/// which this function was called. +/// - line: The line number on which failure occurred. Defaults to the line number on which this +/// function was called. +public func assertStringsEqualWithDiff( + _ actual: String, + _ expected: String, + _ message: String = "", + additionalInfo: @autoclosure () -> String? = nil, + file: StaticString = #filePath, + line: UInt = #line +) { + if actual == expected { + return + } + + failStringsEqualWithDiff( + actual, + expected, + message, + additionalInfo: additionalInfo(), + file: file, + line: line + ) +} + +/// Asserts that the two data are equal, providing Unix `diff`-style output if they are not. +/// +/// - Parameters: +/// - actual: The actual string. +/// - expected: The expected string. +/// - message: An optional description of the failure. +/// - additionalInfo: Additional information about the failed test case that will be printed after the diff +/// - file: The file in which failure occurred. Defaults to the file name of the test case in +/// which this function was called. +/// - line: The line number on which failure occurred. Defaults to the line number on which this +/// function was called. +public func assertDataEqualWithDiff( + _ actual: Data, + _ expected: Data, + _ message: String = "", + additionalInfo: @autoclosure () -> String? = nil, + file: StaticString = #filePath, + line: UInt = #line +) { + if actual == expected { + return + } + + // NOTE: Converting to `Stirng` here looses invalid UTF8 sequence difference, + // but at least we can see something is different. + failStringsEqualWithDiff( + String(decoding: actual, as: UTF8.self), + String(decoding: expected, as: UTF8.self), + message, + additionalInfo: additionalInfo(), + file: file, + line: line + ) +} + +/// `XCTFail` with `diff`-style output. +public func failStringsEqualWithDiff( + _ actual: String, + _ expected: String, + _ message: String = "", + additionalInfo: @autoclosure () -> String? = nil, + file: StaticString = #filePath, + line: UInt = #line +) { + let stringComparison: String + + // Use `CollectionDifference` on supported platforms to get `diff`-like line-based output. On + // older platforms, fall back to simple string comparison. + if #available(macOS 10.15, *) { + let actualLines = actual.components(separatedBy: .newlines) + let expectedLines = expected.components(separatedBy: .newlines) + + let difference = actualLines.difference(from: expectedLines) + + var result = "" + + var insertions = [Int: String]() + var removals = [Int: String]() + + for change in difference { + switch change { + case .insert(let offset, let element, _): + insertions[offset] = element + case .remove(let offset, let element, _): + removals[offset] = element + } + } + + var expectedLine = 0 + var actualLine = 0 + + while expectedLine < expectedLines.count || actualLine < actualLines.count { + if let removal = removals[expectedLine] { + result += "–\(removal)\n" + expectedLine += 1 + } else if let insertion = insertions[actualLine] { + result += "+\(insertion)\n" + actualLine += 1 + } else { + result += " \(expectedLines[expectedLine])\n" + expectedLine += 1 + actualLine += 1 + } + } + + stringComparison = result + } else { + // Fall back to simple message on platforms that don't support CollectionDifference. + stringComparison = """ + Expected: + \(expected) + + Actual: + \(actual) + """ + } + + var fullMessage = """ + \(message.isEmpty ? "Actual output does not match the expected" : message) + \(stringComparison) + """ + if let additional = additionalInfo() { + fullMessage = """ + \(fullMessage) + \(additional) + """ + } + XCTFail(fullMessage, file: file, line: line) +} From bc62af9d235df0bc9e83d1803204b5184fdf7313 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 18 Apr 2024 16:23:54 -0700 Subject: [PATCH 096/159] NFC: Add `Sendable` annotations to PackageDescription APIs --- Sources/PackageDescription/BuildSettings.swift | 12 ++++++------ Sources/PackageDescription/Context.swift | 4 ++-- Sources/PackageDescription/Product.swift | 6 +++--- Sources/PackageDescription/Resource.swift | 4 ++-- Sources/PackageDescription/SupportedPlatforms.swift | 6 +++--- Sources/PackageDescription/Version.swift | 2 +- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Sources/PackageDescription/BuildSettings.swift b/Sources/PackageDescription/BuildSettings.swift index 239f150d53f..e6e3b23ff6e 100644 --- a/Sources/PackageDescription/BuildSettings.swift +++ b/Sources/PackageDescription/BuildSettings.swift @@ -11,7 +11,7 @@ //===----------------------------------------------------------------------===// /// The build configuration, such as debug or release. -public struct BuildConfiguration { +public struct BuildConfiguration: Sendable { /// The configuration of the build. Valid values are `debug` and `release`. let config: String @@ -54,7 +54,7 @@ public struct BuildConfiguration { /// ] /// ), /// ``` -public struct BuildSettingCondition { +public struct BuildSettingCondition: Sendable { /// The applicable platforms for this build setting condition. let platforms: [Platform]? /// The applicable build configuration for this build setting condition. @@ -115,7 +115,7 @@ struct BuildSettingData { } /// A C language build setting. -public struct CSetting { +public struct CSetting: Sendable { /// The abstract build setting data. let data: BuildSettingData @@ -185,7 +185,7 @@ public struct CSetting { } /// A CXX-language build setting. -public struct CXXSetting { +public struct CXXSetting: Sendable { /// The data store for the CXX build setting. let data: BuildSettingData @@ -255,7 +255,7 @@ public struct CXXSetting { } /// A Swift language build setting. -public struct SwiftSetting { +public struct SwiftSetting: Sendable { /// The data store for the Swift build setting. let data: BuildSettingData @@ -407,7 +407,7 @@ public struct SwiftSetting { } /// A linker build setting. -public struct LinkerSetting { +public struct LinkerSetting: Sendable { /// The data store for the Linker setting. let data: BuildSettingData diff --git a/Sources/PackageDescription/Context.swift b/Sources/PackageDescription/Context.swift index eb192f65f19..604e272e534 100644 --- a/Sources/PackageDescription/Context.swift +++ b/Sources/PackageDescription/Context.swift @@ -15,7 +15,7 @@ /// The context encapsulates states that are known when Swift Package Manager interprets the package manifest, /// for example the location in the file system where the current package resides. @available(_PackageDescription, introduced: 5.6) -public struct Context { +public struct Context: Sendable { private static let model = try! ContextModel.decode() /// The directory that contains `Package.swift`. @@ -46,7 +46,7 @@ public struct Context { /// Information about the git status of a given package, if available. @available(_PackageDescription, introduced: 6.0) -public struct GitInformation { +public struct GitInformation: Sendable { public let currentTag: String? public let currentCommit: String public let hasUncommittedChanges: Bool diff --git a/Sources/PackageDescription/Product.swift b/Sources/PackageDescription/Product.swift index 4250817457a..73a533c5203 100644 --- a/Sources/PackageDescription/Product.swift +++ b/Sources/PackageDescription/Product.swift @@ -68,7 +68,7 @@ public class Product { } /// The executable product of a Swift package. - public final class Executable: Product { + public final class Executable: Product, @unchecked Sendable { /// The names of the targets in this product. public let targets: [String] @@ -79,7 +79,7 @@ public class Product { } /// The library product of a Swift package. - public final class Library: Product { + public final class Library: Product, @unchecked Sendable { /// The different types of a library product. public enum LibraryType: String { /// A statically linked library. @@ -105,7 +105,7 @@ public class Product { } /// The plug-in product of a Swift package. - public final class Plugin: Product { + public final class Plugin: Product, @unchecked Sendable { /// The name of the plug-in target to vend as a product. public let targets: [String] diff --git a/Sources/PackageDescription/Resource.swift b/Sources/PackageDescription/Resource.swift index f195a133d45..dfd9272793f 100644 --- a/Sources/PackageDescription/Resource.swift +++ b/Sources/PackageDescription/Resource.swift @@ -33,10 +33,10 @@ /// To learn more about package resources, see /// . @available(_PackageDescription, introduced: 5.3) -public struct Resource { +public struct Resource: Sendable { /// Defines the explicit type of localization for resources. - public enum Localization: String { + public enum Localization: String, Sendable { /// A constant that represents default localization. case `default` diff --git a/Sources/PackageDescription/SupportedPlatforms.swift b/Sources/PackageDescription/SupportedPlatforms.swift index be1ee1462a5..f105e507d07 100644 --- a/Sources/PackageDescription/SupportedPlatforms.swift +++ b/Sources/PackageDescription/SupportedPlatforms.swift @@ -11,7 +11,7 @@ //===----------------------------------------------------------------------===// /// A platform supported by Swift Package Manager. -public struct Platform: Equatable { +public struct Platform: Equatable, Sendable { /// The name of the platform. let name: String @@ -88,7 +88,7 @@ public struct Platform: Equatable { /// package's deployment version. The deployment target of a package's /// dependencies must be lower than or equal to the top-level package's /// deployment target version for a particular platform. -public struct SupportedPlatform: Equatable { +public struct SupportedPlatform: Equatable, Sendable { /// The platform. let platform: Platform @@ -698,7 +698,7 @@ extension SupportedPlatform { } } -fileprivate protocol AppleOSVersion { +fileprivate protocol AppleOSVersion: Sendable { static var name: String { get } static var minimumMajorVersion: Int { get } init(uncheckedVersion: String) diff --git a/Sources/PackageDescription/Version.swift b/Sources/PackageDescription/Version.swift index 661c4f74b3e..4e952063432 100644 --- a/Sources/PackageDescription/Version.swift +++ b/Sources/PackageDescription/Version.swift @@ -35,7 +35,7 @@ /// Increase the third digit of a version, or _patch version_, if you're making /// a backward-compatible bug fix. This allows clients to benefit from bugfixes /// to your package without incurring any maintenance burden. -public struct Version { +public struct Version: Sendable { /// The major version according to the semantic versioning standard. public let major: Int From d30647aa189a6e6e617d3186ed90eed3b9ff59c6 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 22 Apr 2024 20:19:29 -0700 Subject: [PATCH 097/159] When adding a macro target to a package, create a dependency on swift-syntax (#7476) --- .../Commands/PackageCommands/AddTarget.swift | 5 +- .../AddPackageDependency.swift | 20 +++- Sources/PackageModelSyntax/AddTarget.swift | 113 ++++++++++++++---- .../PackageModelSyntax/SyntaxEditUtils.swift | 48 ++++++-- .../ManifestEditTests.swift | 8 ++ 5 files changed, 161 insertions(+), 33 deletions(-) diff --git a/Sources/Commands/PackageCommands/AddTarget.swift b/Sources/Commands/PackageCommands/AddTarget.swift index 0cf0b388820..321fc7ba8ac 100644 --- a/Sources/Commands/PackageCommands/AddTarget.swift +++ b/Sources/Commands/PackageCommands/AddTarget.swift @@ -113,7 +113,10 @@ extension SwiftPackageCommand { let editResult = try PackageModelSyntax.AddTarget.addTarget( target, - to: manifestSyntax + to: manifestSyntax, + installedSwiftPMConfiguration: swiftCommandState + .getHostToolchain() + .installedSwiftPMConfiguration ) try editResult.applyEdits( diff --git a/Sources/PackageModelSyntax/AddPackageDependency.swift b/Sources/PackageModelSyntax/AddPackageDependency.swift index 7ba1889ffe4..09231a49293 100644 --- a/Sources/PackageModelSyntax/AddPackageDependency.swift +++ b/Sources/PackageModelSyntax/AddPackageDependency.swift @@ -44,12 +44,26 @@ public struct AddPackageDependency { throw ManifestEditError.cannotFindPackage } - let edits = try packageCall.appendingToArrayArgument( + let newPackageCall = try addPackageDependencyLocal( + dependency, to: packageCall + ) + + return PackageEditResult( + manifestEdits: [ + .replace(packageCall, with: newPackageCall.description) + ] + ) + } + + /// Implementation of adding a package dependency to an existing call. + static func addPackageDependencyLocal( + _ dependency: PackageDependency, + to packageCall: FunctionCallExprSyntax + ) throws -> FunctionCallExprSyntax { + try packageCall.appendingToArrayArgument( label: "dependencies", trailingLabels: Self.argumentLabelsAfterDependencies, newElement: dependency.asSyntax() ) - - return PackageEditResult(manifestEdits: edits) } } diff --git a/Sources/PackageModelSyntax/AddTarget.swift b/Sources/PackageModelSyntax/AddTarget.swift index 1157775d985..0ae83b78e0c 100644 --- a/Sources/PackageModelSyntax/AddTarget.swift +++ b/Sources/PackageModelSyntax/AddTarget.swift @@ -15,6 +15,7 @@ import PackageModel import SwiftParser import SwiftSyntax import SwiftSyntaxBuilder +import struct TSCUtility.Version /// Add a target to a manifest's source code. public struct AddTarget { @@ -34,7 +35,8 @@ public struct AddTarget { /// new target. public static func addTarget( _ target: TargetDescription, - to manifest: SourceFileSyntax + to manifest: SourceFileSyntax, + installedSwiftPMConfiguration: InstalledSwiftPMConfiguration = .default ) throws -> PackageEditResult { // Make sure we have a suitable tools version in the manifest. try manifest.checkEditManifestToolsVersion() @@ -53,7 +55,7 @@ public struct AddTarget { target.dependencies.append(contentsOf: macroTargetDependencies) } - let manifestEdits = try packageCall.appendingToArrayArgument( + var newPackageCall = try packageCall.appendingToArrayArgument( label: "targets", trailingLabels: Self.argumentLabelsAfterTargets, newElement: target.asSyntax() @@ -66,7 +68,11 @@ public struct AddTarget { } guard let outerDirectory else { - return PackageEditResult(manifestEdits: manifestEdits) + return PackageEditResult( + manifestEdits: [ + .replace(packageCall, with: newPackageCall.description) + ] + ) } let outerPath = try RelativePath(validating: outerDirectory) @@ -82,31 +88,49 @@ public struct AddTarget { ) // Perform any other actions that are needed for this target type. + var extraManifestEdits: [SourceEdit] = [] switch target.type { case .macro: - // Macros need a file that introduces the main entrypoint - // describing all of the macros. - auxiliaryFiles.addSourceFile( - path: outerPath.appending( - components: [target.name, "ProvidedMacros.swift"] - ), - sourceCode: """ - import SwiftCompilerPlugin - - @main - struct \(raw: target.name)Macros: CompilerPlugin { - let providingMacros: [Macro.Type] = [ - \(raw: target.name).self, - ] - } - """ + addProvidedMacrosSourceFile( + outerPath: outerPath, + target: target, + to: &auxiliaryFiles ) + if !manifest.description.contains("swift-syntax") { + newPackageCall = try AddPackageDependency + .addPackageDependencyLocal( + .swiftSyntax( + configuration: installedSwiftPMConfiguration + ), + to: newPackageCall + ) + + // Look for the first import declaration and insert an + // import of `CompilerPluginSupport` there. + let newImport = "import CompilerPluginSupport\n" + for node in manifest.statements { + if let importDecl = node.item.as(ImportDeclSyntax.self) { + let insertPos = importDecl + .positionAfterSkippingLeadingTrivia + extraManifestEdits.append( + SourceEdit( + range: insertPos.. PackageDependency { + let swiftSyntaxVersionDefault = configuration + .swiftSyntaxVersionForMacroTemplate + let swiftSyntaxVersion = Version(swiftSyntaxVersionDefault.description)! + + return .sourceControl( + identity: PackageIdentity(url: swiftSyntaxURL), + nameForTargetDependencyResolutionOnly: nil, + location: .remote(swiftSyntaxURL), + requirement: .range(.upToNextMajor(from: swiftSyntaxVersion)), + productFilter: .everything + ) + } +} diff --git a/Sources/PackageModelSyntax/SyntaxEditUtils.swift b/Sources/PackageModelSyntax/SyntaxEditUtils.swift index 774853a46f7..27c7ed475ae 100644 --- a/Sources/PackageModelSyntax/SyntaxEditUtils.swift +++ b/Sources/PackageModelSyntax/SyntaxEditUtils.swift @@ -81,6 +81,11 @@ extension FunctionCallExprSyntax { func findArgument(labeled label: String) -> LabeledExprSyntax? { arguments.first { $0.label?.text == label } } + + /// Find a call argument index based on its label. + func findArgumentIndex(labeled label: String) -> LabeledExprListSyntax.Index? { + arguments.firstIndex { $0.label?.text == label } + } } extension LabeledExprListSyntax { @@ -358,6 +363,35 @@ extension Array { } // MARK: Utilities for adding arguments into calls. +fileprivate class ReplacingRewriter: SyntaxRewriter { + let childNode: Syntax + let newChildNode: Syntax + + init(childNode: Syntax, newChildNode: Syntax) { + self.childNode = childNode + self.newChildNode = newChildNode + super.init() + } + + override func visitAny(_ node: Syntax) -> Syntax? { + if node == childNode { + return newChildNode + } + + return nil + } +} + +fileprivate extension SyntaxProtocol { + /// Replace the given child with a new child node. + func replacingChild(_ childNode: Syntax, with newChildNode: Syntax) -> Self { + return ReplacingRewriter( + childNode: childNode, + newChildNode: newChildNode + ).rewrite(self).cast(Self.self) + } +} + extension FunctionCallExprSyntax { /// Produce source edits that will add the given new element to the /// array for an argument with the given label (if there is one), or @@ -371,12 +405,12 @@ extension FunctionCallExprSyntax { /// which helps determine where the argument should be inserted if /// it doesn't exist yet. /// - newElement: The new element. - /// - Returns: the resulting source edits to make this change. + /// - Returns: the function call after making this change. func appendingToArrayArgument( label: String, trailingLabels: Set, newElement: ExprSyntax - ) throws -> [SourceEdit] { + ) throws -> FunctionCallExprSyntax { // If there is already an argument with this name, append to the array // literal in there. if let arg = findArgument(labeled: label) { @@ -402,7 +436,8 @@ extension FunctionCallExprSyntax { element: formattedElement, outerLeadingTrivia: arg.leadingTrivia ) - return [ .replace(argArray, with: updatedArgArray.description) ] + + return replacingChild(Syntax(argArray), with: Syntax(updatedArgArray)) } // There was no argument, so we need to create one. @@ -452,11 +487,6 @@ extension FunctionCallExprSyntax { ) } - return [ - SourceEdit.replace( - arguments, - with: newArguments.description - ) - ] + return with(\.arguments, newArguments) } } diff --git a/Tests/PackageModelSyntaxTests/ManifestEditTests.swift b/Tests/PackageModelSyntaxTests/ManifestEditTests.swift index c61a6036007..1e78a26be06 100644 --- a/Tests/PackageModelSyntaxTests/ManifestEditTests.swift +++ b/Tests/PackageModelSyntaxTests/ManifestEditTests.swift @@ -465,14 +465,22 @@ class ManifestEditTests: XCTestCase { func testAddMacroTarget() throws { try assertManifestRefactor(""" // swift-tools-version: 5.5 + import PackageDescription + let package = Package( name: "packages" ) """, expectedManifest: """ // swift-tools-version: 5.5 + import CompilerPluginSupport + import PackageDescription + let package = Package( name: "packages", + dependencies: [ + .package(url: "https://github.com/apple/swift-syntax.git", from: "600.0.0-latest"), + ], targets: [ .macro( name: "MyMacro", From a4080540e9dd5935e17a1c3d1bd92d5acd9ebf14 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 22 Apr 2024 23:47:14 -0700 Subject: [PATCH 098/159] [SE-0301] Add swift package add-product command and supporting library (#7477) Introduce support for adding a new product to the package manifest, both programmatically (via PackageModelSyntax) and via the `swift package add-product` command. Help for this command is: OVERVIEW: Add a new product to the manifest USAGE: swift package add-product [--type ] [--targets ...] [--url ] [--path ] [--checksum ] ARGUMENTS: The name of the new product OPTIONS: --type The type of target to add, which can be one of 'executable', 'library', 'static-library', 'dynamic-library', or 'plugin' (default: library) --targets A list of targets that are part of this product --url The URL for a remote binary target --path The path to a local binary target --checksum The checksum for a remote binary target --version Show the version. -h, -help, --help Show help information. --- Sources/Commands/CMakeLists.txt | 1 + .../Commands/PackageCommands/AddProduct.swift | 118 ++++++++++++++++++ .../Commands/PackageCommands/AddTarget.swift | 6 +- .../PackageCommands/SwiftPackageCommand.swift | 1 + Sources/PackageModelSyntax/AddProduct.swift | 58 +++++++++ Sources/PackageModelSyntax/CMakeLists.txt | 2 + .../ProductDescription+Syntax.swift | 61 +++++++++ .../PackageModelSyntax/SyntaxEditUtils.swift | 41 ++++-- Tests/CommandsTests/PackageCommandTests.swift | 30 +++++ .../ManifestEditTests.swift | 39 ++++++ 10 files changed, 345 insertions(+), 12 deletions(-) create mode 100644 Sources/Commands/PackageCommands/AddProduct.swift create mode 100644 Sources/PackageModelSyntax/AddProduct.swift create mode 100644 Sources/PackageModelSyntax/ProductDescription+Syntax.swift diff --git a/Sources/Commands/CMakeLists.txt b/Sources/Commands/CMakeLists.txt index f7978b6d84e..75a97da70c6 100644 --- a/Sources/Commands/CMakeLists.txt +++ b/Sources/Commands/CMakeLists.txt @@ -8,6 +8,7 @@ add_library(Commands PackageCommands/AddDependency.swift + PackageCommands/AddProduct.swift PackageCommands/AddTarget.swift PackageCommands/APIDiff.swift PackageCommands/ArchiveSource.swift diff --git a/Sources/Commands/PackageCommands/AddProduct.swift b/Sources/Commands/PackageCommands/AddProduct.swift new file mode 100644 index 00000000000..92329fb0a2d --- /dev/null +++ b/Sources/Commands/PackageCommands/AddProduct.swift @@ -0,0 +1,118 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import ArgumentParser +import Basics +import CoreCommands +import PackageModel +import PackageModelSyntax +import SwiftParser +import SwiftSyntax +import TSCBasic +import TSCUtility +import Workspace + +extension SwiftPackageCommand { + struct AddProduct: SwiftCommand { + /// The package product type used for the command-line. This is a + /// subset of `ProductType` that expands out the library types. + enum CommandProductType: String, Codable, ExpressibleByArgument { + case executable + case library + case staticLibrary = "static-library" + case dynamicLibrary = "dynamic-library" + case plugin + } + + package static let configuration = CommandConfiguration( + abstract: "Add a new product to the manifest") + + @OptionGroup(visibility: .hidden) + var globalOptions: GlobalOptions + + @Argument(help: "The name of the new product") + var name: String + + @Option(help: "The type of target to add, which can be one of 'executable', 'library', 'static-library', 'dynamic-library', or 'plugin'") + var type: CommandProductType = .library + + @Option( + parsing: .upToNextOption, + help: "A list of targets that are part of this product" + ) + var targets: [String] = [] + + @Option(help: "The URL for a remote binary target") + var url: String? + + @Option(help: "The path to a local binary target") + var path: String? + + @Option(help: "The checksum for a remote binary target") + var checksum: String? + + func run(_ swiftCommandState: SwiftCommandState) throws { + let workspace = try swiftCommandState.getActiveWorkspace() + + guard let packagePath = try swiftCommandState.getWorkspaceRoot().packages.first else { + throw StringError("unknown package") + } + + // Load the manifest file + let fileSystem = workspace.fileSystem + let manifestPath = packagePath.appending("Package.swift") + let manifestContents: ByteString + do { + manifestContents = try fileSystem.readFileContents(manifestPath) + } catch { + throw StringError("cannot find package manifest in \(manifestPath)") + } + + // Parse the manifest. + let manifestSyntax = manifestContents.withData { data in + data.withUnsafeBytes { buffer in + buffer.withMemoryRebound(to: UInt8.self) { buffer in + Parser.parse(source: buffer) + } + } + } + + // Map the product type. + let type: ProductType = switch self.type { + case .executable: .executable + case .library: .library(.automatic) + case .dynamicLibrary: .library(.dynamic) + case .staticLibrary: .library(.static) + case .plugin: .plugin + } + + let product = try ProductDescription( + name: name, + type: type, + targets: targets + ) + + let editResult = try PackageModelSyntax.AddProduct.addProduct( + product, + to: manifestSyntax + ) + + try editResult.applyEdits( + to: fileSystem, + manifest: manifestSyntax, + manifestPath: manifestPath, + verbose: !globalOptions.logging.quiet + ) + } + } +} + diff --git a/Sources/Commands/PackageCommands/AddTarget.swift b/Sources/Commands/PackageCommands/AddTarget.swift index 321fc7ba8ac..0bd46584f57 100644 --- a/Sources/Commands/PackageCommands/AddTarget.swift +++ b/Sources/Commands/PackageCommands/AddTarget.swift @@ -28,8 +28,6 @@ extension SwiftPackageCommand { case library case executable case test - case binary - case plugin case macro } @@ -42,7 +40,7 @@ extension SwiftPackageCommand { @Argument(help: "The name of the new target") var name: String - @Option(help: "The type of target to add, which can be one of ") + @Option(help: "The type of target to add, which can be one of 'library', 'executable', 'test', or 'macro'") var type: TargetType = .library @Option( @@ -91,8 +89,6 @@ extension SwiftPackageCommand { case .library: .regular case .executable: .executable case .test: .test - case .binary: .binary - case .plugin: .plugin case .macro: .macro } diff --git a/Sources/Commands/PackageCommands/SwiftPackageCommand.swift b/Sources/Commands/PackageCommands/SwiftPackageCommand.swift index d2ca89f5b3c..e34da31471c 100644 --- a/Sources/Commands/PackageCommands/SwiftPackageCommand.swift +++ b/Sources/Commands/PackageCommands/SwiftPackageCommand.swift @@ -36,6 +36,7 @@ package struct SwiftPackageCommand: AsyncParsableCommand { version: SwiftVersion.current.completeDisplayString, subcommands: [ AddDependency.self, + AddProduct.self, AddTarget.self, Clean.self, PurgeCache.self, diff --git a/Sources/PackageModelSyntax/AddProduct.swift b/Sources/PackageModelSyntax/AddProduct.swift new file mode 100644 index 00000000000..3e058232f3a --- /dev/null +++ b/Sources/PackageModelSyntax/AddProduct.swift @@ -0,0 +1,58 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import PackageModel +import SwiftParser +import SwiftSyntax +import SwiftSyntaxBuilder + +/// Add a product to the manifest's source code. +public struct AddProduct { + /// The set of argument labels that can occur after the "products" + /// argument in the Package initializers. + /// + /// TODO: Could we generate this from the the PackageDescription module, so + /// we don't have keep it up-to-date manually? + private static let argumentLabelsAfterProducts: Set = [ + "dependencies", + "targets", + "swiftLanguageVersions", + "cLanguageStandard", + "cxxLanguageStandard" + ] + + /// Produce the set of source edits needed to add the given package + /// dependency to the given manifest file. + public static func addProduct( + _ product: ProductDescription, + to manifest: SourceFileSyntax + ) throws -> PackageEditResult { + // Make sure we have a suitable tools version in the manifest. + try manifest.checkEditManifestToolsVersion() + + guard let packageCall = manifest.findCall(calleeName: "Package") else { + throw ManifestEditError.cannotFindPackage + } + + let newPackageCall = try packageCall.appendingToArrayArgument( + label: "products", + trailingLabels: argumentLabelsAfterProducts, + newElement: product.asSyntax() + ) + + return PackageEditResult( + manifestEdits: [ + .replace(packageCall, with: newPackageCall.description) + ] + ) + } +} diff --git a/Sources/PackageModelSyntax/CMakeLists.txt b/Sources/PackageModelSyntax/CMakeLists.txt index 3a968e84d7e..cc1f9fb6c20 100644 --- a/Sources/PackageModelSyntax/CMakeLists.txt +++ b/Sources/PackageModelSyntax/CMakeLists.txt @@ -8,11 +8,13 @@ add_library(PackageModelSyntax AddPackageDependency.swift + AddProduct.swift AddTarget.swift ManifestEditError.swift ManifestSyntaxRepresentable.swift PackageDependency+Syntax.swift PackageEditResult.swift + ProductDescription+Syntax.swift SyntaxEditUtils.swift TargetDescription+Syntax.swift ) diff --git a/Sources/PackageModelSyntax/ProductDescription+Syntax.swift b/Sources/PackageModelSyntax/ProductDescription+Syntax.swift new file mode 100644 index 00000000000..eed6650dfce --- /dev/null +++ b/Sources/PackageModelSyntax/ProductDescription+Syntax.swift @@ -0,0 +1,61 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Basics +import PackageModel +import SwiftSyntax +import SwiftParser + +extension ProductDescription: ManifestSyntaxRepresentable { + /// The function name in the package manifest. + /// + /// Some of these are actually invalid, but it's up to the caller + /// to check the precondition. + private var functionName: String { + switch type { + case .executable: "executable" + case .library(_): "library" + case .macro: "macro" + case .plugin: "plugin" + case .snippet: "snippet" + case .test: "test" + } + } + + func asSyntax() -> ExprSyntax { + var arguments: [LabeledExprSyntax] = [] + arguments.append(label: "name", stringLiteral: name) + + // Libraries have a type. + if case .library(let libraryType) = type { + switch libraryType { + case .automatic: + break + + case .dynamic, .static: + arguments.append( + label: "type", + expression: ".\(raw: libraryType.rawValue)" + ) + } + } + + arguments.appendIfNonEmpty( + label: "targets", + arrayLiteral: targets + ) + + let separateParen: String = arguments.count > 1 ? "\n" : "" + let argumentsSyntax = LabeledExprListSyntax(arguments) + return ".\(raw: functionName)(\(argumentsSyntax)\(raw: separateParen))" + } +} diff --git a/Sources/PackageModelSyntax/SyntaxEditUtils.swift b/Sources/PackageModelSyntax/SyntaxEditUtils.swift index 27c7ed475ae..df64aa4c2d3 100644 --- a/Sources/PackageModelSyntax/SyntaxEditUtils.swift +++ b/Sources/PackageModelSyntax/SyntaxEditUtils.swift @@ -25,6 +25,16 @@ extension Trivia { var hasNewlines: Bool { contains(where: \.isNewline) } + + /// Produce trivia from the last newline to the end, dropping anything + /// prior to that. + func onlyLastLine() -> Trivia { + guard let lastNewline = pieces.lastIndex(where: { $0.isNewline }) else { + return self + } + + return Trivia(pieces: pieces[lastNewline...]) + } } /// Syntax walker to find the first occurrence of a given node kind that @@ -186,7 +196,7 @@ extension ArrayExprSyntax { if let last = elements.last { // The leading trivia of the new element should match that of the // last element. - leadingTrivia = last.leadingTrivia + leadingTrivia = last.leadingTrivia.onlyLastLine() // Add a trailing comma to the last element if it isn't already // there. @@ -324,14 +334,31 @@ extension Array { elements.append(expression: element.asSyntax()) } - // When we have more than one element in the array literal, we add - // newlines at the beginning of each element. Do the same for the - // right square bracket. - let rightSquareLeadingTrivia: Trivia = elements.count > 0 - ? .newline - : Trivia() + // Figure out the trivia for the left and right square + let leftSquareTrailingTrivia: Trivia + let rightSquareLeadingTrivia: Trivia + switch elements.count { + case 0: + // Put a single space between the square brackets. + leftSquareTrailingTrivia = Trivia() + rightSquareLeadingTrivia = .space + + case 1: + // Put spaces around the single element + leftSquareTrailingTrivia = .space + rightSquareLeadingTrivia = .space + + default: + // Each of the elements will have a leading newline. Add a leading + // newline before the close bracket. + leftSquareTrailingTrivia = Trivia() + rightSquareLeadingTrivia = .newline + } let array = ArrayExprSyntax( + leftSquare: .leftSquareToken( + trailingTrivia: leftSquareTrailingTrivia + ), elements: ArrayElementListSyntax(elements), rightSquare: .rightSquareToken( leadingTrivia: rightSquareLeadingTrivia diff --git a/Tests/CommandsTests/PackageCommandTests.swift b/Tests/CommandsTests/PackageCommandTests.swift index 82c4810ef1d..be29ac86dd5 100644 --- a/Tests/CommandsTests/PackageCommandTests.swift +++ b/Tests/CommandsTests/PackageCommandTests.swift @@ -851,6 +851,36 @@ final class PackageCommandTests: CommandsTestCase { } } + func testPackageAddProduct() throws { + try testWithTemporaryDirectory { tmpPath in + let fs = localFileSystem + let path = tmpPath.appending("PackageB") + try fs.createDirectory(path) + + try fs.writeFileContents(path.appending("Package.swift"), string: + """ + // swift-tools-version: 5.9 + import PackageDescription + let package = Package( + name: "client" + ) + """ + ) + + _ = try execute(["add-product", "MyLib", "--targets", "MyLib", "--type", "static-library"], packagePath: path) + + let manifest = path.appending("Package.swift") + XCTAssertFileExists(manifest) + let contents: String = try fs.readFileContents(manifest) + + XCTAssertMatch(contents, .contains(#"products:"#)) + XCTAssertMatch(contents, .contains(#".library"#)) + XCTAssertMatch(contents, .contains(#"name: "MyLib""#)) + XCTAssertMatch(contents, .contains(#"type: .static"#)) + XCTAssertMatch(contents, .contains(#"targets:"#)) + XCTAssertMatch(contents, .contains(#""MyLib""#)) + } + } func testPackageEditAndUnedit() throws { try fixture(name: "Miscellaneous/PackageEdit") { fixturePath in let fooPath = fixturePath.appending("foo") diff --git a/Tests/PackageModelSyntaxTests/ManifestEditTests.swift b/Tests/PackageModelSyntaxTests/ManifestEditTests.swift index 1e78a26be06..2acb6debd62 100644 --- a/Tests/PackageModelSyntaxTests/ManifestEditTests.swift +++ b/Tests/PackageModelSyntaxTests/ManifestEditTests.swift @@ -335,6 +335,43 @@ class ManifestEditTests: XCTestCase { } } + func testAddLibraryProduct() throws { + try assertManifestRefactor(""" + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + targets: [ + .target(name: "MyLib"), + ], + ) + """, + expectedManifest: """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + products: [ + .library( + name: "MyLib", + type: .dynamic, + targets: [ "MyLib" ] + ), + ], + targets: [ + .target(name: "MyLib"), + ], + ) + """) { manifest in + try AddProduct.addProduct( + ProductDescription( + name: "MyLib", + type: .library(.dynamic), + targets: [ "MyLib" ] + ), + to: manifest + ) + } + } + func testAddLibraryTarget() throws { try assertManifestRefactor(""" // swift-tools-version: 5.5 @@ -412,6 +449,7 @@ class ManifestEditTests: XCTestCase { let package = Package( name: "packages", targets: [ + // These are the targets .target(name: "MyLib") ] ) @@ -421,6 +459,7 @@ class ManifestEditTests: XCTestCase { let package = Package( name: "packages", targets: [ + // These are the targets .target(name: "MyLib"), .executableTarget( name: "MyProgram", From d59191a38878304561fe76176061521b7234b605 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 23 Apr 2024 17:20:29 +0100 Subject: [PATCH 099/159] Make `SwiftSDK.init` available via `@_spi` (#7482) This initializer is an internal implementation detail that should only be available via `@_spi`. --- Sources/Commands/SwiftTestCommand.swift | 4 +++- Sources/PackageModel/SwiftSDKs/SwiftSDK.swift | 8 +++++--- Tests/BuildTests/BuildPlanTests.swift | 3 +++ Tests/PackageModelTests/PackageModelTests.swift | 5 ++++- Tests/PackageModelTests/SwiftSDKTests.swift | 5 ++++- Tests/SPMBuildCoreTests/PluginInvocationTests.swift | 7 +++++-- 6 files changed, 24 insertions(+), 8 deletions(-) diff --git a/Sources/Commands/SwiftTestCommand.swift b/Sources/Commands/SwiftTestCommand.swift index 1650c2eb663..afe0e9e6c45 100644 --- a/Sources/Commands/SwiftTestCommand.swift +++ b/Sources/Commands/SwiftTestCommand.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2015-2022 Apple Inc. and the Swift project authors +// Copyright (c) 2015-2024 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information @@ -19,6 +19,8 @@ import CoreCommands import Dispatch import Foundation import PackageGraph + +@_spi(SwiftPMInternal) import PackageModel import SPMBuildCore diff --git a/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift b/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift index 665c4fe1351..cb929a88350 100644 --- a/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift +++ b/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2014-2021 Apple Inc. and the Swift project authors +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information @@ -156,7 +156,8 @@ public struct SwiftSDK: Equatable { } /// Whether or not the receiver supports testing using XCTest. - package enum XCTestSupport: Sendable, Equatable { + @_spi(SwiftPMInternal) + public enum XCTestSupport: Sendable, Equatable { /// XCTest is supported. case supported @@ -465,7 +466,8 @@ public struct SwiftSDK: Equatable { } /// Creates a Swift SDK with the specified properties. - package init( + @_spi(SwiftPMInternal) + public init( hostTriple: Triple? = nil, targetTriple: Triple? = nil, toolset: Toolset, diff --git a/Tests/BuildTests/BuildPlanTests.swift b/Tests/BuildTests/BuildPlanTests.swift index 4a8b5072387..6ce71ad87e5 100644 --- a/Tests/BuildTests/BuildPlanTests.swift +++ b/Tests/BuildTests/BuildPlanTests.swift @@ -20,7 +20,10 @@ import DriverSupport @testable import PackageGraph import PackageLoading + +@_spi(SwiftPMInternal) @testable import PackageModel + import SPMBuildCore import SPMTestSupport import SwiftDriver diff --git a/Tests/PackageModelTests/PackageModelTests.swift b/Tests/PackageModelTests/PackageModelTests.swift index 54c32fa276e..7ba89f6927d 100644 --- a/Tests/PackageModelTests/PackageModelTests.swift +++ b/Tests/PackageModelTests/PackageModelTests.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2014-2021 Apple Inc. and the Swift project authors +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information @@ -11,7 +11,10 @@ //===----------------------------------------------------------------------===// import Basics + +@_spi(SwiftPMInternal) @testable import PackageModel + import func TSCBasic.withTemporaryFile import XCTest diff --git a/Tests/PackageModelTests/SwiftSDKTests.swift b/Tests/PackageModelTests/SwiftSDKTests.swift index a5b27fdfba6..1ddcd9c6f50 100644 --- a/Tests/PackageModelTests/SwiftSDKTests.swift +++ b/Tests/PackageModelTests/SwiftSDKTests.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2014-2022 Apple Inc. and the Swift project authors +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information @@ -11,7 +11,10 @@ //===----------------------------------------------------------------------===// @testable import Basics + +@_spi(SwiftPMInternal) @testable import PackageModel + @testable import SPMBuildCore import XCTest diff --git a/Tests/SPMBuildCoreTests/PluginInvocationTests.swift b/Tests/SPMBuildCoreTests/PluginInvocationTests.swift index a0deb0d6652..58668f9e5db 100644 --- a/Tests/SPMBuildCoreTests/PluginInvocationTests.swift +++ b/Tests/SPMBuildCoreTests/PluginInvocationTests.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2021-2022 Apple Inc. and the Swift project authors +// Copyright (c) 2021-2024 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information @@ -16,7 +16,10 @@ import Basics @testable import PackageGraph import PackageLoading + +@_spi(SwiftPMInternal) import PackageModel + @testable import SPMBuildCore import SPMTestSupport import Workspace @@ -26,7 +29,7 @@ import class TSCBasic.InMemoryFileSystem import struct TSCUtility.SerializedDiagnostics -class PluginInvocationTests: XCTestCase { +final class PluginInvocationTests: XCTestCase { func testBasics() throws { // Construct a canned file system and package graph with a single package and a library that uses a build tool plugin that invokes a tool. From 4f83c6d555a6606904ffa1635986aa4d586dbd8c Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Wed, 24 Apr 2024 00:00:12 -0700 Subject: [PATCH 100/159] Only export required SwiftSyntax targets (#7489) Also, use `SPMSwiftSyntax::` namespace to avoid potential name conflict issues. --- Sources/PackageModelSyntax/CMakeLists.txt | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Sources/PackageModelSyntax/CMakeLists.txt b/Sources/PackageModelSyntax/CMakeLists.txt index cc1f9fb6c20..556fd0c619b 100644 --- a/Sources/PackageModelSyntax/CMakeLists.txt +++ b/Sources/PackageModelSyntax/CMakeLists.txt @@ -48,16 +48,10 @@ set(SWIFT_SYNTAX_MODULES SwiftParserDiagnostics SwiftDiagnostics SwiftSyntax - SwiftOperators SwiftSyntaxBuilder - SwiftSyntaxMacros - SwiftSyntaxMacroExpansion - SwiftCompilerPluginMessageHandling - # Support for LSP SwiftIDEUtils - SwiftRefactor ) export(TARGETS ${SWIFT_SYNTAX_MODULES} - NAMESPACE SwiftSyntax:: + NAMESPACE SPMSwiftSyntax:: FILE ${CMAKE_BINARY_DIR}/cmake/modules/SwiftSyntaxConfig.cmake EXPORT_LINK_INTERFACE_LIBRARIES) \ No newline at end of file From b9eb3c138ec681d8858de4d8bf7d179758490ba1 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 25 Apr 2024 00:22:47 +0100 Subject: [PATCH 101/159] Include host triple test modules in `ResolvedPackage` (#7493) Modified `ResolvedModule` build triples were not reflected in `ResolvedPackage`, which excluded those modules from the build graph. Verified manually with `swift-foundation` and `swift-testing` packages. More comprehensive automated tests will be included in a future PR. Resolves https://github.com/apple/swift-package-manager/issues/7479. --- Sources/Basics/Errors.swift | 3 +-- .../SwiftTargetBuildDescription.swift | 5 +++-- .../LLBuildManifestBuilder+Resources.swift | 2 +- .../Build/BuildManifest/LLBuildManifestBuilder.swift | 4 ++-- Sources/Build/BuildPlan/BuildPlan.swift | 2 +- Sources/PackageGraph/ModulesGraph+Loading.swift | 12 ++++++++---- .../PackageGraph/Resolution/ResolvedPackage.swift | 4 ++-- .../ClangTargetBuildDescriptionTests.swift | 2 +- Tests/CommandsTests/PackageCommandTests.swift | 8 ++++---- 9 files changed, 23 insertions(+), 19 deletions(-) diff --git a/Sources/Basics/Errors.swift b/Sources/Basics/Errors.swift index c0900f565d7..f075978da86 100644 --- a/Sources/Basics/Errors.swift +++ b/Sources/Basics/Errors.swift @@ -21,8 +21,7 @@ public struct InternalError: Error { private let description: String public init(_ description: String) { assertionFailure(description) - self - .description = + self.description = "Internal error. Please file a bug at https://github.com/apple/swift-package-manager/issues with this info. \(description)" } } diff --git a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift index 286e6307dbc..7c9c6ff0b35 100644 --- a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift @@ -65,9 +65,10 @@ package final class SwiftTargetBuildDescription { /// Path to the bundle generated for this module (if any). var bundlePath: AbsolutePath? { if let bundleName = target.underlying.potentialBundleName, needsResourceBundle { - return self.defaultBuildParameters.bundlePath(named: bundleName) + let suffix = self.defaultBuildParameters.suffix(triple: self.target.buildTriple) + return self.defaultBuildParameters.bundlePath(named: bundleName + suffix) } else { - return .none + return nil } } diff --git a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Resources.swift b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Resources.swift index 599c7c435d5..df561d46b8b 100644 --- a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Resources.swift +++ b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Resources.swift @@ -45,7 +45,7 @@ extension LLBuildManifestBuilder { outputs.append(output) } - let cmdName = target.target.getLLBuildResourcesCmdName(config: target.buildParameters.buildConfig) + let cmdName = target.target.getLLBuildResourcesCmdName(buildParameters: target.buildParameters) self.manifest.addPhonyCmd(name: cmdName, inputs: outputs, outputs: [.virtual(cmdName)]) return .virtual(cmdName) diff --git a/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift b/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift index e9c1070b21c..edf8415c123 100644 --- a/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift +++ b/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift @@ -325,8 +325,8 @@ extension ResolvedModule { "\(self.name)-\(buildParameters.buildConfig)\(buildParameters.suffix(triple: self.buildTriple)).module" } - package func getLLBuildResourcesCmdName(config: String) -> String { - "\(self.name)-\(config).module-resources" + package func getLLBuildResourcesCmdName(buildParameters: BuildParameters) -> String { + "\(self.name)-\(buildParameters.buildConfig)\(buildParameters.suffix(triple: self.buildTriple)).module-resources" } } diff --git a/Sources/Build/BuildPlan/BuildPlan.swift b/Sources/Build/BuildPlan/BuildPlan.swift index 0f4a9e06e8d..8f03a297328 100644 --- a/Sources/Build/BuildPlan/BuildPlan.swift +++ b/Sources/Build/BuildPlan/BuildPlan.swift @@ -699,7 +699,7 @@ extension Basics.Diagnostic { extension BuildParameters { /// Returns a named bundle's path inside the build directory. func bundlePath(named name: String) -> AbsolutePath { - buildPath.appending(component: name + self.triple.nsbundleExtension) + self.buildPath.appending(component: name + self.triple.nsbundleExtension) } } diff --git a/Sources/PackageGraph/ModulesGraph+Loading.swift b/Sources/PackageGraph/ModulesGraph+Loading.swift index 1079ad868b0..59687da73f3 100644 --- a/Sources/PackageGraph/ModulesGraph+Loading.swift +++ b/Sources/PackageGraph/ModulesGraph+Loading.swift @@ -1050,13 +1050,17 @@ private final class ResolvedPackageBuilder: ResolvedBuilder { } override func constructImpl() throws -> ResolvedPackage { - return ResolvedPackage( + let products = try self.products.map { try $0.construct() } + var targets = products.reduce(into: IdentifiableSet()) { $0.formUnion($1.targets) } + try targets.formUnion(self.targets.map { try $0.construct() }) + + return try ResolvedPackage( underlying: self.package, defaultLocalization: self.defaultLocalization, supportedPlatforms: self.supportedPlatforms, - dependencies: try self.dependencies.map{ try $0.construct() }, - targets: try self.targets.map{ try $0.construct() }, - products: try self.products.map{ try $0.construct() }, + dependencies: self.dependencies.map{ try $0.construct() }, + targets: targets, + products: products, registryMetadata: self.registryMetadata, platformVersionProvider: self.platformVersionProvider ) diff --git a/Sources/PackageGraph/Resolution/ResolvedPackage.swift b/Sources/PackageGraph/Resolution/ResolvedPackage.swift index 5506bcc15b1..78cd87c2345 100644 --- a/Sources/PackageGraph/Resolution/ResolvedPackage.swift +++ b/Sources/PackageGraph/Resolution/ResolvedPackage.swift @@ -34,7 +34,7 @@ public struct ResolvedPackage { public let underlying: Package /// The targets contained in the package. - public let targets: [ResolvedModule] + public let targets: IdentifiableSet /// The products produced by the package. public let products: [ResolvedProduct] @@ -58,7 +58,7 @@ public struct ResolvedPackage { defaultLocalization: String?, supportedPlatforms: [SupportedPlatform], dependencies: [ResolvedPackage], - targets: [ResolvedModule], + targets: IdentifiableSet, products: [ResolvedProduct], registryMetadata: RegistryReleaseMetadata?, platformVersionProvider: PlatformVersionProvider diff --git a/Tests/BuildTests/ClangTargetBuildDescriptionTests.swift b/Tests/BuildTests/ClangTargetBuildDescriptionTests.swift index f8361e473df..32b24c92fc4 100644 --- a/Tests/BuildTests/ClangTargetBuildDescriptionTests.swift +++ b/Tests/BuildTests/ClangTargetBuildDescriptionTests.swift @@ -106,7 +106,7 @@ final class ClangTargetBuildDescriptionTests: XCTestCase { defaultLocalization: nil, supportedPlatforms: [], dependencies: [], - targets: [target], + targets: .init([target]), products: [], registryMetadata: nil, platformVersionProvider: .init(implementation: .minimumDeploymentTargetDefault)), diff --git a/Tests/CommandsTests/PackageCommandTests.swift b/Tests/CommandsTests/PackageCommandTests.swift index be29ac86dd5..43a71bcf1a7 100644 --- a/Tests/CommandsTests/PackageCommandTests.swift +++ b/Tests/CommandsTests/PackageCommandTests.swift @@ -3054,9 +3054,9 @@ final class PackageCommandTests: CommandsTestCase { let execProducts = context.package.products(ofType: ExecutableProduct.self) print("execProducts: \\(execProducts.map{ $0.name })") let swiftTargets = context.package.targets(ofType: SwiftSourceModuleTarget.self) - print("swiftTargets: \\(swiftTargets.map{ $0.name })") + print("swiftTargets: \\(swiftTargets.map{ $0.name }.sorted())") let swiftSources = swiftTargets.flatMap{ $0.sourceFiles(withSuffix: ".swift") } - print("swiftSources: \\(swiftSources.map{ $0.path.lastComponent })") + print("swiftSources: \\(swiftSources.map{ $0.path.lastComponent }.sorted())") if let target = target.sourceModule { print("Module kind of '\\(target.name)': \\(target.kind)") @@ -3120,8 +3120,8 @@ final class PackageCommandTests: CommandsTestCase { do { let (stdout, _) = try SwiftPM.Package.execute(["print-target-dependencies", "--target", "FifthTarget"], packagePath: packageDir) XCTAssertMatch(stdout, .contains("execProducts: [\"FifthTarget\"]")) - XCTAssertMatch(stdout, .contains("swiftTargets: [\"ThirdTarget\", \"TestTarget\", \"SecondTarget\", \"FourthTarget\", \"FirstTarget\", \"FifthTarget\"]")) - XCTAssertMatch(stdout, .contains("swiftSources: [\"library.swift\", \"tests.swift\", \"library.swift\", \"library.swift\", \"library.swift\", \"main.swift\"]")) + XCTAssertMatch(stdout, .contains("swiftTargets: [\"FifthTarget\", \"FirstTarget\", \"FourthTarget\", \"SecondTarget\", \"TestTarget\", \"ThirdTarget\"]")) + XCTAssertMatch(stdout, .contains("swiftSources: [\"library.swift\", \"library.swift\", \"library.swift\", \"library.swift\", \"main.swift\", \"tests.swift\"]")) XCTAssertMatch(stdout, .contains("Module kind of 'FifthTarget': executable")) } From dbd894521ad595036c2912bb056ebd4b6a3336c3 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Thu, 25 Apr 2024 09:18:06 +0900 Subject: [PATCH 102/159] build: remove the requirement to checkout swift-syntax (#7486) In the case that a shared copy of swift-syntax is being used to build, allow the sources to be unavailable. If the user specifies `SWIFTPM_PATH_TO_SWIFT_SYNTAX_SOURCE`, that will be preferred, otherwise, we fallback to the upstream git repository. --- BuildSupport/SwiftSyntax/CMakeLists.txt | 21 +++++++++------------ Utilities/bootstrap | 2 ++ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/BuildSupport/SwiftSyntax/CMakeLists.txt b/BuildSupport/SwiftSyntax/CMakeLists.txt index 7a3cfbfd4de..09f4cd51d2c 100644 --- a/BuildSupport/SwiftSyntax/CMakeLists.txt +++ b/BuildSupport/SwiftSyntax/CMakeLists.txt @@ -1,17 +1,14 @@ -SET(SWIFTPM_PATH_TO_SWIFT_SYNTAX_SOURCE ${CMAKE_SOURCE_DIR}/../swift-syntax) -message(STATUS "swift-syntax path: ${SWIFTPM_PATH_TO_SWIFT_SYNTAX_SOURCE}") - include(FetchContent) -if(NOT EXISTS "${SWIFTPM_PATH_TO_SWIFT_SYNTAX_SOURCE}") - message(SEND_ERROR "swift-syntax is required to build SwiftPM. Please run update-checkout or specify SWIFTPM_PATH_TO_SWIFT_SYNTAX_SOURCE") - return() -endif() - -# Build swift-syntax libraries with FetchContent. -# set(CMAKE_Swift_COMPILER_TARGET ${SWIFT_HOST_TRIPLE}) set(BUILD_SHARED_LIBS OFF) -file(TO_CMAKE_PATH "${SWIFTPM_PATH_TO_SWIFT_SYNTAX_SOURCE}" swift_syntax_path) -FetchContent_Declare(SwiftSyntax SOURCE_DIR "${swift_syntax_path}") +if(DEFINED SWIFTPM_PATH_TO_SWIFT_SYNTAX_SOURCE) + file(TO_CMAKE_PATH "${SWIFTPM_PATH_TO_SWIFT_SYNTAX_SOURCE}" swift_syntax_path) + FetchContent_Declare(SwiftSyntax + SOURCE_DIR "${swift_syntax_path}") +else() + FetchContent_Declare(SwiftSyntax + GIT_REPOSITORY https://github.com/apple/swift-syntax + GIT_TAG main) +endif() FetchContent_MakeAvailable(SwiftSyntax) diff --git a/Utilities/bootstrap b/Utilities/bootstrap index 2da5c06d725..893a5f8beaa 100755 --- a/Utilities/bootstrap +++ b/Utilities/bootstrap @@ -202,6 +202,7 @@ def parse_global_args(args): args.source_dirs["swift-collections"] = os.path.join(args.project_root, "..", "swift-collections") args.source_dirs["swift-certificates"] = os.path.join(args.project_root, "..", "swift-certificates") args.source_dirs["swift-asn1"] = os.path.join(args.project_root, "..", "swift-asn1") + args.source_dirs["swift-syntax"] = os.path.join(args.project_root, "..", "swift-syntax") args.source_root = os.path.join(args.project_root, "Sources") if platform.system() == 'Darwin': @@ -608,6 +609,7 @@ def build_swiftpm_with_cmake(args): "-DSwiftCrypto_DIR=" + os.path.join(args.build_dirs["swift-crypto"], "cmake/modules"), "-DSwiftASN1_DIR=" + os.path.join(args.build_dirs["swift-asn1"], "cmake/modules"), "-DSwiftCertificates_DIR=" + os.path.join(args.build_dirs["swift-certificates"], "cmake/modules"), + "-DSWIFTPM_PATH_TO_SWIFT_SYNTAX_SOURCE=" + args.source_dirs["swift-syntax"], ] if platform.system() == 'Darwin': From 6a7e578c1e03492fc072dc555f7a72acf6ab88e5 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Thu, 25 Apr 2024 11:59:20 -0700 Subject: [PATCH 103/159] Set environment variable to disable building swift-syntax in Swift 6 mode (#7495) This allows us to work around the following issue in CI: The self-hosted SwiftPM job has Xcode 15.3 (Swift 5.10) installed and builds a Swift 6 SwiftPM from source. It then tries to build itself as a fat binary using the just-built Swift 6 SwiftPM, which uses xcbuild from Xcode as the build system. But the xcbuild in the installed Xcode is too old and doesn't know about Swift 6 mode, so it fails with: SWIFT_VERSION '6' is unsupported, supported versions are: 4.0, 4.2, 5.0 (rdar://126952308). This is fixed by setting `SWIFTSYNTAX_DISABLE_SWIFT_6_MODE` in `build-using-self`. The source compat suite is seeing the same issue, just by using `bootstrap`, so set it there as well. Accompanies https://github.com/apple/swift-syntax/pull/2621 --- Utilities/bootstrap | 2 ++ Utilities/build-using-self | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Utilities/bootstrap b/Utilities/bootstrap index 893a5f8beaa..8af6ec7e6d0 100755 --- a/Utilities/bootstrap +++ b/Utilities/bootstrap @@ -747,6 +747,8 @@ def get_swiftpm_env_cmd(args): env_cmd.append("SWIFTPM_LLBUILD_FWK=1") env_cmd.append("SWIFTCI_USE_LOCAL_DEPS=1") env_cmd.append("SWIFTPM_MACOS_DEPLOYMENT_TARGET=%s" % g_macos_deployment_target) + # Disable Swift 6 mode in swift-syntax to work around rdar://126952308 + env_cmd.append("SWIFTSYNTAX_DISABLE_SWIFT_6_MODE=1") if not '-macosx' in args.build_target and args.command == 'install': env_cmd.append("SWIFTCI_INSTALL_RPATH_OS=%s" % args.platform_path.group(2)) diff --git a/Utilities/build-using-self b/Utilities/build-using-self index 1d6a1552993..57a7eed54c2 100755 --- a/Utilities/build-using-self +++ b/Utilities/build-using-self @@ -20,6 +20,8 @@ echo "Current directory is ${PWD}" CONFIGURATION=debug export SWIFTCI_IS_SELF_HOSTED=1 +# Disable Swift 6 mode in swift-syntax to work around rdar://126952308 +export SWIFTSYNTAX_DISABLE_SWIFT_6_MODE=1 set -x From 3f9587c3930b2b910635ffeab05339d3c5c6c86d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Boris=20B=C3=BCgling?= Date: Thu, 25 Apr 2024 15:04:44 -0700 Subject: [PATCH 104/159] Handle Swift versions unsupported by XCBuild (#7499) XCBuild has its own notion of supported Swift versions which can differ from the ones SwiftPM's own build system and the used compiler support. Using a version outside of the supported range prevents building, so we'll try to obtain the list of supported versions and intersect it with the versions supported by a given package to find the best version supported by both. --- Sources/PackageLoading/PackageBuilder.swift | 163 ++-- Sources/PackageModel/Target/SwiftTarget.swift | 26 +- Sources/XCBuildSupport/PIFBuilder.swift | 564 +++++++---- Sources/XCBuildSupport/XcodeBuildSystem.swift | 109 ++- .../XCBuildSupportTests/PIFBuilderTests.swift | 914 ++++++++++++------ 5 files changed, 1179 insertions(+), 597 deletions(-) diff --git a/Sources/PackageLoading/PackageBuilder.swift b/Sources/PackageLoading/PackageBuilder.swift index c81ccb1fadd..488a25cb0a2 100644 --- a/Sources/PackageLoading/PackageBuilder.swift +++ b/Sources/PackageLoading/PackageBuilder.swift @@ -16,8 +16,8 @@ import OrderedCollections import PackageModel import func TSCBasic.findCycle -import func TSCBasic.topologicalSort import struct TSCBasic.KeyedPair +import func TSCBasic.topologicalSort /// An error in the structure or layout of a package. public enum ModuleError: Swift.Error { @@ -162,7 +162,9 @@ extension ModuleError: CustomStringConvertible { targetsDescription += " and \(targets.count - 3) others" } return """ - multiple similar targets \(targetsDescription) appear in registry package '\(registryPackage)' and source control package '\(scmPackage)', \ + multiple similar targets \(targetsDescription) appear in registry package '\( + registryPackage + )' and source control package '\(scmPackage)', \ this may indicate that the two packages are the same and can be de-duplicated \ by activating the automatic source-control to registry replacement, or by using mirrors. \ if they are not duplicate consider using the `moduleAliases` parameter in manifest to provide unique names @@ -175,11 +177,11 @@ extension ModuleError.InvalidLayoutType: CustomStringConvertible { public var description: String { switch self { case .multipleSourceRoots(let paths): - return "multiple source roots found: " + paths.map(\.description).sorted().joined(separator: ", ") + "multiple source roots found: " + paths.map(\.description).sorted().joined(separator: ", ") case .modulemapInSources(let path): - return "modulemap '\(path)' should be inside the 'include' directory" + "modulemap '\(path)' should be inside the 'include' directory" case .modulemapMissing(let path): - return "missing system target module map at '\(path)'" + "missing system target module map at '\(path)'" } } } @@ -203,9 +205,9 @@ extension Target.Error: CustomStringConvertible { var description: String { switch self { case .invalidName(let path, let problem): - return "invalid target name at '\(path)'; \(problem)" + "invalid target name at '\(path)'; \(problem)" case .mixedSources(let path): - return "target at '\(path)' contains mixed language source files; feature not supported" + "target at '\(path)' contains mixed language source files; feature not supported" } } } @@ -214,7 +216,7 @@ extension Target.Error.ModuleNameProblem: CustomStringConvertible { var description: String { switch self { case .emptyName: - return "target names can not be empty" + "target names can not be empty" } } } @@ -231,9 +233,9 @@ extension Product.Error: CustomStringConvertible { var description: String { switch self { case .emptyName: - return "product names can not be empty" + "product names can not be empty" case .moduleEmpty(let product, let target): - return "target '\(target)' referenced in product '\(product)' is empty" + "target '\(target)' referenced in product '\(product)' is empty" } } } @@ -313,6 +315,10 @@ public final class PackageBuilder { // The set of the sources computed so far, used to validate source overlap private var allSources = Set() + // Caches all declared versions for this package. + private var declaredSwiftVersionsCache: [SwiftLanguageVersion]? = nil + + // Caches the version we chose to build for. private var swiftVersionCache: SwiftLanguageVersion? = nil /// Create a builder for the given manifest and package `path`. @@ -497,15 +503,15 @@ public final class PackageBuilder { -> (targetDir: String, testTargetDir: String, pluginTargetDir: String) { let targetDir = PackageBuilder.predefinedSourceDirectories.first(where: { - fileSystem.isDirectory(packagePath.appending(component: $0)) + self.fileSystem.isDirectory(self.packagePath.appending(component: $0)) }) ?? PackageBuilder.predefinedSourceDirectories[0] let testTargetDir = PackageBuilder.predefinedTestDirectories.first(where: { - fileSystem.isDirectory(packagePath.appending(component: $0)) + self.fileSystem.isDirectory(self.packagePath.appending(component: $0)) }) ?? PackageBuilder.predefinedTestDirectories[0] let pluginTargetDir = PackageBuilder.predefinedPluginDirectories.first(where: { - fileSystem.isDirectory(packagePath.appending(component: $0)) + self.fileSystem.isDirectory(self.packagePath.appending(component: $0)) }) ?? PackageBuilder.predefinedPluginDirectories[0] return (targetDir, testTargetDir, pluginTargetDir) @@ -558,14 +564,13 @@ public final class PackageBuilder { } // Check if target is present in the predefined directory. - let predefinedDir: PredefinedTargetDirectory - switch target.type { + let predefinedDir: PredefinedTargetDirectory = switch target.type { case .test: - predefinedDir = predefinedTestTargetDirectory + predefinedTestTargetDirectory case .plugin: - predefinedDir = predefinedPluginTargetDirectory + predefinedPluginTargetDirectory default: - predefinedDir = predefinedTargetDirectory + predefinedTargetDirectory } let path = predefinedDir.path.appending(component: target.name) @@ -600,7 +605,12 @@ public final class PackageBuilder { let potentialTargets: [PotentialModule] potentialTargets = try self.manifest.targetsRequired(for: self.productFilter).map { target in let path = try findPath(for: target) - return PotentialModule(name: target.name, path: path, type: target.type, packageAccess: target.packageAccess) + return PotentialModule( + name: target.name, + path: path, + type: target.type, + packageAccess: target.packageAccess + ) } let targets = try createModules(potentialTargets) @@ -639,12 +649,13 @@ public final class PackageBuilder { let products = Dictionary(manifest.products.map { ($0.name, $0) }, uniquingKeysWith: { $1 }) - // If there happens to be a plugin product with the right name in the same package, we want to use that automatically. + // If there happens to be a plugin product with the right name in the same package, we want to use that + // automatically. func pluginTargetName(for productName: String) -> String? { if let product = products[productName], product.type == .plugin { - return product.targets.first + product.targets.first } else { - return nil + nil } } @@ -659,12 +670,12 @@ public final class PackageBuilder { // Since we already checked above that all referenced targets // has to present, we always expect this target to be present in // potentialModules dictionary. - return potentialModuleMap[name]! + potentialModuleMap[name]! case .product: - return nil + nil case .byName(let name, _): // By name dependency may or may not be a target dependency. - return potentialModuleMap[name] + potentialModuleMap[name] } } // If there are plugin usages, consider them to be dependencies too. @@ -672,16 +683,16 @@ public final class PackageBuilder { successors += pluginUsages.compactMap { switch $0 { case .plugin(_, .some(_)): - return nil + nil case .plugin(let name, nil): if let potentialModule = potentialModuleMap[name] { - return potentialModule + potentialModule } else if let targetName = pluginTargetName(for: name), let potentialModule = potentialModuleMap[targetName] { - return potentialModule + potentialModule } else { - return nil + nil } } } @@ -885,7 +896,7 @@ public final class PackageBuilder { // Compute the path to public headers directory. let publicHeaderComponent = manifestTarget.publicHeadersPath ?? ClangTarget.defaultPublicHeadersComponent - let publicHeadersPath = potentialModule.path.appending(try RelativePath(validating: publicHeaderComponent)) + let publicHeadersPath = try potentialModule.path.appending(RelativePath(validating: publicHeaderComponent)) guard publicHeadersPath.isDescendantOfOrEqual(to: potentialModule.path) else { throw ModuleError.invalidPublicHeadersDirectory(potentialModule.name) } @@ -960,7 +971,7 @@ public final class PackageBuilder { // Create and return the right kind of target depending on what kind of sources we found. if sources.hasSwiftSources { - return SwiftTarget( + return try SwiftTarget( name: potentialModule.name, potentialBundleName: potentialBundleName, type: targetType, @@ -971,15 +982,18 @@ public final class PackageBuilder { others: others, dependencies: dependencies, packageAccess: potentialModule.packageAccess, - swiftVersion: try self.swiftVersion(), + swiftVersion: self.swiftVersion(), + declaredSwiftVersions: self.declaredSwiftVersions(), buildSettings: buildSettings, buildSettingsDescription: manifestTarget.settings, usesUnsafeFlags: manifestTarget.usesUnsafeFlags ) } else { - // It's not a Swift target, so it's a Clang target (those are the only two types of source target currently supported). + // It's not a Swift target, so it's a Clang target (those are the only two types of source target currently + // supported). - // First determine the type of module map that will be appropriate for the target based on its header layout. + // First determine the type of module map that will be appropriate for the target based on its header + // layout. let moduleMapType: ModuleMapType if self.fileSystem.exists(publicHeadersPath) { @@ -1027,8 +1041,7 @@ public final class PackageBuilder { for target: TargetDescription?, targetRoot: AbsolutePath, cxxLanguageStandard: String? = nil - ) throws -> BuildSettings.AssignmentTable - { + ) throws -> BuildSettings.AssignmentTable { var table = BuildSettings.AssignmentTable() guard let target else { return table } @@ -1098,7 +1111,8 @@ public final class PackageBuilder { } if lang == .Cxx { - values = ["-cxx-interoperability-mode=default"] + (cxxLanguageStandard.flatMap { ["-Xcc", "-std=\($0)"] } ?? []) + values = ["-cxx-interoperability-mode=default"] + + (cxxLanguageStandard.flatMap { ["-Xcc", "-std=\($0)"] } ?? []) } else { values = [] } @@ -1174,38 +1188,52 @@ public final class PackageBuilder { if let platforms = condition?.platformNames.map({ if let platform = platformRegistry.platformByName[$0] { - return platform + platform } else { - return PackageModel.Platform.custom(name: $0, oldestSupportedVersion: .unknown) + PackageModel.Platform.custom(name: $0, oldestSupportedVersion: .unknown) } - }), !platforms.isEmpty - { + }), !platforms.isEmpty { conditions.append(.init(platforms: platforms)) } return conditions } - /// Computes the swift version to use for this manifest. - private func swiftVersion() throws -> SwiftLanguageVersion { - if let swiftVersion = self.swiftVersionCache { - return swiftVersion + private func declaredSwiftVersions() throws -> [SwiftLanguageVersion] { + if let versions = self.declaredSwiftVersionsCache { + return versions } - let computedSwiftVersion: SwiftLanguageVersion - - // Figure out the swift version from declared list in the manifest. + let versions: [SwiftLanguageVersion] if let swiftLanguageVersions = manifest.swiftLanguageVersions { - guard let swiftVersion = swiftLanguageVersions.sorted(by: >).first(where: { $0 <= ToolsVersion.current }) - else { + versions = swiftLanguageVersions.sorted(by: >).filter { $0 <= ToolsVersion.current } + + if versions.isEmpty { throw ModuleError.incompatibleToolsVersions( package: self.identity.description, required: swiftLanguageVersions, current: .current ) } - computedSwiftVersion = swiftVersion + } else { + versions = [] + } + + self.declaredSwiftVersionsCache = versions + return versions + } + + /// Computes the swift version to use for this manifest. + private func swiftVersion() throws -> SwiftLanguageVersion { + if let swiftVersion = self.swiftVersionCache { + return swiftVersion + } + + // Figure out the swift version from declared list in the manifest. + let declaredSwiftVersions = try declaredSwiftVersions() + let computedSwiftVersion: SwiftLanguageVersion = if let declaredSwiftVersion = declaredSwiftVersions.first { + declaredSwiftVersion } else { // Otherwise, use the version depending on the manifest version. - computedSwiftVersion = self.manifest.toolsVersion.swiftLanguageVersion + self.manifest.toolsVersion.swiftLanguageVersion } self.swiftVersionCache = computedSwiftVersion return computedSwiftVersion @@ -1253,9 +1281,9 @@ public final class PackageBuilder { } // If we have already searched this path, skip. if !pathsSearched.contains(searchPath) { - SwiftTarget.testEntryPointNames.forEach { name in + for name in SwiftTarget.testEntryPointNames { let path = searchPath.appending(component: name) - if fileSystem.isFile(path) { + if self.fileSystem.isFile(path) { testEntryPointFiles.insert(path) } } @@ -1351,12 +1379,11 @@ public final class PackageBuilder { // First add explicit products. - let filteredProducts: [ProductDescription] - switch self.productFilter { + let filteredProducts: [ProductDescription] = switch self.productFilter { case .everything: - filteredProducts = self.manifest.products + self.manifest.products case .specific(let set): - filteredProducts = self.manifest.products.filter { set.contains($0.name) } + self.manifest.products.filter { set.contains($0.name) } } for product in filteredProducts { if product.name.isEmpty { @@ -1554,11 +1581,11 @@ public final class PackageBuilder { // These are static constants, safe to access by index; the first choice is preferred. switch type { case .test: - return self.predefinedTestDirectories[0] + self.predefinedTestDirectories[0] case .plugin: - return self.predefinedPluginDirectories[0] + self.predefinedPluginDirectories[0] default: - return self.predefinedSourceDirectories[0] + self.predefinedSourceDirectories[0] } } } @@ -1602,9 +1629,9 @@ extension Manifest { [target.name] + target.dependencies.compactMap { switch $0 { case .target(let name, _): - return name + name case .byName, .product: - return nil + nil } } } @@ -1650,9 +1677,9 @@ extension Target.Dependency { fileprivate var nameAndType: String { switch self { case .target: - return "target-\(name)" + "target-\(name)" case .product: - return "product-\(name)" + "product-\(name)" } } } @@ -1667,7 +1694,7 @@ extension PackageBuilder { } return try walk(snippetsDirectory, fileSystem: self.fileSystem) - .filter { fileSystem.isFile($0) && $0.extension == "swift" } + .filter { self.fileSystem.isFile($0) && $0.extension == "swift" } .map { sourceFile in let name = sourceFile.basenameWithoutExt let sources = Sources(paths: [sourceFile], root: sourceFile.parentDirectory) @@ -1689,14 +1716,14 @@ extension PackageBuilder { targetRoot: sourceFile.parentDirectory ) - return SwiftTarget( + return try SwiftTarget( name: name, type: .snippet, path: .root, sources: sources, dependencies: dependencies, packageAccess: false, - swiftVersion: try swiftVersion(), + swiftVersion: self.swiftVersion(), buildSettings: buildSettings, buildSettingsDescription: targetDescription.settings, usesUnsafeFlags: false diff --git a/Sources/PackageModel/Target/SwiftTarget.swift b/Sources/PackageModel/Target/SwiftTarget.swift index 0dd16dbed91..c7428bbbb5b 100644 --- a/Sources/PackageModel/Target/SwiftTarget.swift +++ b/Sources/PackageModel/Target/SwiftTarget.swift @@ -24,6 +24,7 @@ public final class SwiftTarget: Target { public init(name: String, dependencies: [Target.Dependency], packageAccess: Bool, testDiscoverySrc: Sources) { self.swiftVersion = .v5 + self.declaredSwiftVersions = [] super.init( name: name, @@ -42,6 +43,9 @@ public final class SwiftTarget: Target { /// The swift version of this target. public let swiftVersion: SwiftLanguageVersion + /// The list of swift versions declared by the manifest. + public let declaredSwiftVersions: [SwiftLanguageVersion] + public init( name: String, potentialBundleName: String? = nil, @@ -54,12 +58,14 @@ public final class SwiftTarget: Target { dependencies: [Target.Dependency] = [], packageAccess: Bool, swiftVersion: SwiftLanguageVersion, + declaredSwiftVersions: [SwiftLanguageVersion] = [], buildSettings: BuildSettings.AssignmentTable = .init(), buildSettingsDescription: [TargetBuildSettingDescription.Setting] = [], pluginUsages: [PluginUsage] = [], usesUnsafeFlags: Bool ) { self.swiftVersion = swiftVersion + self.declaredSwiftVersions = declaredSwiftVersions super.init( name: name, potentialBundleName: potentialBundleName, @@ -79,7 +85,12 @@ public final class SwiftTarget: Target { } /// Create an executable Swift target from test entry point file. - public init(name: String, dependencies: [Target.Dependency], packageAccess: Bool, testEntryPointPath: AbsolutePath) { + public init( + name: String, + dependencies: [Target.Dependency], + packageAccess: Bool, + testEntryPointPath: AbsolutePath + ) { // Look for the first swift test target and use the same swift version // for linux main target. This will need to change if we move to a model // where we allow per target swift language version build settings. @@ -92,7 +103,9 @@ public final class SwiftTarget: Target { // We need to select the latest Swift language version that can // satisfy the current tools version but there is not a good way to // do that currently. - self.swiftVersion = swiftTestTarget?.swiftVersion ?? SwiftLanguageVersion(string: String(SwiftVersion.current.major)) ?? .v4 + self.swiftVersion = swiftTestTarget? + .swiftVersion ?? SwiftLanguageVersion(string: String(SwiftVersion.current.major)) ?? .v4 + self.declaredSwiftVersions = [] let sources = Sources(paths: [testEntryPointPath], root: testEntryPointPath.parentDirectory) super.init( @@ -111,17 +124,20 @@ public final class SwiftTarget: Target { private enum CodingKeys: String, CodingKey { case swiftVersion + case declaredSwiftVersions } - public override func encode(to encoder: Encoder) throws { + override public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(swiftVersion, forKey: .swiftVersion) + try container.encode(self.swiftVersion, forKey: .swiftVersion) + try container.encode(self.declaredSwiftVersions, forKey: .declaredSwiftVersions) try super.encode(to: encoder) } - required public init(from decoder: Decoder) throws { + public required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.swiftVersion = try container.decode(SwiftLanguageVersion.self, forKey: .swiftVersion) + self.declaredSwiftVersions = try container.decode([SwiftLanguageVersion].self, forKey: .declaredSwiftVersions) try super.init(from: decoder) } diff --git a/Sources/XCBuildSupport/PIFBuilder.swift b/Sources/XCBuildSupport/PIFBuilder.swift index 3d77fd3b95d..0790e336238 100644 --- a/Sources/XCBuildSupport/PIFBuilder.swift +++ b/Sources/XCBuildSupport/PIFBuilder.swift @@ -10,16 +10,16 @@ // //===----------------------------------------------------------------------===// -import Foundation import Basics -import PackageModel -import PackageLoading +import Foundation import PackageGraph +import PackageLoading +import PackageModel import SPMBuildCore -import func TSCBasic.topologicalSort import func TSCBasic.memoize +import func TSCBasic.topologicalSort /// The parameters required by `PIFBuilder`. struct PIFBuilderParameters { @@ -40,11 +40,13 @@ struct PIFBuilderParameters { /// The toolchain's SDK root path. let sdkRootPath: AbsolutePath? + + /// The Swift language versions supported by the XCBuild being used for the buid. + let supportedSwiftVersions: [SwiftLanguageVersion] } /// PIF object builder for a package graph. public final class PIFBuilder { - /// Name of the PIF target aggregating all targets (excluding tests). public static let allExcludingTestsTargetName = "AllExcludingTests" @@ -108,14 +110,15 @@ public final class PIFBuilder { /// Constructs a `PIF.TopLevelObject` representing the package graph. public func construct() throws -> PIF.TopLevelObject { - try memoize(to: &pif) { - let rootPackage = self.graph.rootPackages[graph.rootPackages.startIndex] + try memoize(to: &self.pif) { + let rootPackage = self.graph.rootPackages[self.graph.rootPackages.startIndex] - let sortedPackages = graph.packages.sorted { $0.manifest.displayName < $1.manifest.displayName } // TODO: use identity instead? + let sortedPackages = self.graph.packages + .sorted { $0.manifest.displayName < $1.manifest.displayName } // TODO: use identity instead? var projects: [PIFProjectBuilder] = try sortedPackages.map { package in try PackagePIFProjectBuilder( package: package, - parameters: parameters, + parameters: self.parameters, fileSystem: self.fileSystem, observabilityScope: self.observabilityScope ) @@ -123,11 +126,11 @@ public final class PIFBuilder { projects.append(AggregatePIFProjectBuilder(projects: projects)) - let workspace = PIF.Workspace( + let workspace = try PIF.Workspace( guid: "Workspace:\(rootPackage.path.pathString)", - name: rootPackage.manifest.displayName, // TODO: use identity instead? + name: rootPackage.manifest.displayName, // TODO: use identity instead? path: rootPackage.path, - projects: try projects.map { try $0.construct() } + projects: projects.map { try $0.construct() } ) return PIF.TopLevelObject(workspace: workspace) @@ -142,8 +145,8 @@ public final class PIFBuilder { observabilityScope: ObservabilityScope, preservePIFModelStructure: Bool ) throws -> String { - let parameters = PIFBuilderParameters(buildParameters) - let builder = Self.init( + let parameters = PIFBuilderParameters(buildParameters, supportedSwiftVersions: []) + let builder = Self( graph: packageGraph, parameters: parameters, fileSystem: fileSystem, @@ -170,9 +173,9 @@ class PIFProjectBuilder { var developmentRegion: String fileprivate init() { - groupTree = PIFGroupBuilder(path: "") - targets = [] - buildConfigurations = [] + self.groupTree = PIFGroupBuilder(path: "") + self.targets = [] + self.buildConfigurations = [] } /// Creates and adds a new empty build configuration, i.e. one that does not initially have any build settings. @@ -181,10 +184,15 @@ class PIFProjectBuilder { func addBuildConfiguration( name: String, settings: PIF.BuildSettings = PIF.BuildSettings(), - impartedBuildProperties: PIF.ImpartedBuildProperties = PIF.ImpartedBuildProperties(settings: PIF.BuildSettings()) + impartedBuildProperties: PIF.ImpartedBuildProperties = PIF + .ImpartedBuildProperties(settings: PIF.BuildSettings()) ) -> PIFBuildConfigurationBuilder { - let builder = PIFBuildConfigurationBuilder(name: name, settings: settings, impartedBuildProperties: impartedBuildProperties) - buildConfigurations.append(builder) + let builder = PIFBuildConfigurationBuilder( + name: name, + settings: settings, + impartedBuildProperties: impartedBuildProperties + ) + self.buildConfigurations.append(builder) return builder } @@ -200,34 +208,34 @@ class PIFProjectBuilder { productName: String ) -> PIFTargetBuilder { let target = PIFTargetBuilder(guid: guid, name: name, productType: productType, productName: productName) - targets.append(target) + self.targets.append(target) return target } @discardableResult func addAggregateTarget(guid: PIF.GUID, name: String) -> PIFAggregateTargetBuilder { let target = PIFAggregateTargetBuilder(guid: guid, name: name) - targets.append(target) + self.targets.append(target) return target } func construct() throws -> PIF.Project { let buildConfigurations = self.buildConfigurations.map { builder -> PIF.BuildConfiguration in - builder.guid = "\(guid)::BUILDCONFIG_\(builder.name)" + builder.guid = "\(self.guid)::BUILDCONFIG_\(builder.name)" return builder.construct() } // Construct group tree before targets to make sure file references have GUIDs. - groupTree.guid = "\(guid)::MAINGROUP" + groupTree.guid = "\(self.guid)::MAINGROUP" let groupTree = self.groupTree.construct() as! PIF.Group let targets = try self.targets.map { try $0.construct() } return PIF.Project( - guid: guid, - name: name, - path: path, - projectDirectory: projectDirectory, - developmentRegion: developmentRegion, + guid: self.guid, + name: self.name, + path: self.path, + projectDirectory: self.projectDirectory, + developmentRegion: self.developmentRegion, buildConfigurations: buildConfigurations, targets: targets, groupTree: groupTree @@ -243,7 +251,7 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { private var binaryGroup: PIFGroupBuilder! private let executableTargetProductMap: [ResolvedModule.ID: ResolvedProduct] - var isRootPackage: Bool { package.manifest.packageKind.isRoot } + var isRootPackage: Bool { self.package.manifest.packageKind.isRoot } init( package: ResolvedPackage, @@ -349,24 +357,24 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { addBuildConfiguration(name: "Release", settings: releaseSettings) for product in package.products.sorted(by: { $0.name < $1.name }) { - try addTarget(for: product) + try self.addTarget(for: product) } for target in package.targets.sorted(by: { $0.name < $1.name }) { try self.addTarget(for: target) } - if binaryGroup.children.isEmpty { - groupTree.removeChild(binaryGroup) + if self.binaryGroup.children.isEmpty { + groupTree.removeChild(self.binaryGroup) } } private func addTarget(for product: ResolvedProduct) throws { switch product.type { case .executable, .snippet, .test: - try addMainModuleTarget(for: product) + try self.addMainModuleTarget(for: product) case .library: - addLibraryTarget(for: product) + self.addLibraryTarget(for: product) case .plugin, .macro: return } @@ -395,18 +403,18 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { } private func targetName(for product: ResolvedProduct) -> String { - return Self.targetName(for: product.name) + Self.targetName(for: product.name) } static func targetName(for productName: String) -> String { - return "\(productName)_\(String(productName.hash, radix: 16, uppercase: true))_PackageProduct" + "\(productName)_\(String(productName.hash, radix: 16, uppercase: true))_PackageProduct" } private func addMainModuleTarget(for product: ResolvedProduct) throws { let productType: PIF.Target.ProductType = product.type == .executable ? .executable : .unitTest - let pifTarget = addTarget( + let pifTarget = self.addTarget( guid: product.pifTargetGUID, - name: targetName(for: product), + name: self.targetName(for: product), productType: productType, productName: product.name ) @@ -414,11 +422,11 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { // We'll be infusing the product's main module target into the one for the product itself. let mainTarget = product.mainTarget - addSources(mainTarget.sources, to: pifTarget) + self.addSources(mainTarget.sources, to: pifTarget) let dependencies = try! topologicalSort(mainTarget.dependencies) { $0.packageDependencies }.sorted() for dependency in dependencies { - addDependency(to: dependency, in: pifTarget, linkProduct: true) + self.addDependency(to: dependency, in: pifTarget, linkProduct: true) } // Configure the target-wide build settings. The details depend on the kind of product we're building, but are @@ -434,7 +442,10 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { settings[.DEFINES_MODULE] = "YES" if product.type == .executable || product.type == .test { - settings[.LIBRARY_SEARCH_PATHS] = ["$(inherited)", "\(parameters.toolchainLibDir.pathString)/swift/macosx"] + settings[.LIBRARY_SEARCH_PATHS] = [ + "$(inherited)", + "\(self.parameters.toolchainLibDir.pathString)/swift/macosx", + ] } // Tests can have a custom deployment target based on the minimum supported by XCTest. @@ -452,7 +463,7 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { settings[.SUPPORTED_PLATFORMS] = ["macosx", "linux"] // Setup install path for executables if it's in root of a pure Swift package. - if isRootPackage { + if self.isRootPackage { settings[.SKIP_INSTALL] = "NO" settings[.INSTALL_PATH] = "/usr/local/bin" settings[.LD_RUNPATH_SEARCH_PATHS, default: ["$(inherited)"]].append("@executable_path/../lib") @@ -471,7 +482,9 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { settings[.GCC_C_LANGUAGE_STANDARD] = clangTarget.cLanguageStandard settings[.CLANG_CXX_LANGUAGE_STANDARD] = clangTarget.cxxLanguageStandard } else if let swiftTarget = mainTarget.underlying as? SwiftTarget { - settings[.SWIFT_VERSION] = swiftTarget.swiftVersion.description + settings[.SWIFT_VERSION] = try swiftTarget + .computeEffectiveSwiftVersion(supportedSwiftVersions: self.parameters.supportedSwiftVersions) + .description settings.addCommonSwiftSettings(package: self.package, target: mainTarget, parameters: self.parameters) } @@ -487,7 +500,7 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { var releaseSettings = settings var impartedSettings = PIF.BuildSettings() - try addManifestBuildSettings( + try self.addManifestBuildSettings( from: mainTarget.underlying, debugSettings: &debugSettings, releaseSettings: &releaseSettings, @@ -495,8 +508,16 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { ) let impartedBuildProperties = PIF.ImpartedBuildProperties(settings: impartedSettings) - pifTarget.addBuildConfiguration(name: "Debug", settings: debugSettings, impartedBuildProperties: impartedBuildProperties) - pifTarget.addBuildConfiguration(name: "Release", settings: releaseSettings, impartedBuildProperties: impartedBuildProperties) + pifTarget.addBuildConfiguration( + name: "Debug", + settings: debugSettings, + impartedBuildProperties: impartedBuildProperties + ) + pifTarget.addBuildConfiguration( + name: "Release", + settings: releaseSettings, + impartedBuildProperties: impartedBuildProperties + ) } private func addLibraryTarget(for product: ResolvedProduct) { @@ -525,7 +546,7 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { // depends. let pifTarget = self.addTarget( guid: product.pifTargetGUID, - name: targetName(for: product), + name: self.targetName(for: product), productType: productType, productName: pifTargetProductName ) @@ -537,10 +558,10 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { switch dependency { case .target(let target, let conditions): if target.type != .systemModule { - addDependency(to: target, in: pifTarget, conditions: conditions, linkProduct: true) + self.addDependency(to: target, in: pifTarget, conditions: conditions, linkProduct: true) } case .product(let product, let conditions): - addDependency(to: product, in: pifTarget, conditions: conditions, linkProduct: true) + self.addDependency(to: product, in: pifTarget, conditions: conditions, linkProduct: true) } } @@ -565,9 +586,12 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { settings[.DEFINES_MODULE] = "YES" settings[.SKIP_INSTALL] = "NO" settings[.INSTALL_PATH] = "/usr/local/lib" - settings[.LIBRARY_SEARCH_PATHS] = ["$(inherited)", "\(parameters.toolchainLibDir.pathString)/swift/macosx"] + settings[.LIBRARY_SEARCH_PATHS] = [ + "$(inherited)", + "\(self.parameters.toolchainLibDir.pathString)/swift/macosx", + ] - if !parameters.shouldCreateDylibForDynamicProducts { + if !self.parameters.shouldCreateDylibForDynamicProducts { settings[.GENERATE_INFOPLIST_FILE] = "YES" // If the built framework is named same as one of the target in the package, it can be picked up // automatically during indexing since the build system always adds a -F flag to the built products dir. @@ -590,7 +614,7 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { } private func addLibraryTarget(for target: ResolvedModule) throws { - let pifTarget = addTarget( + let pifTarget = self.addTarget( guid: target.pifTargetGUID, name: target.name, productType: .objectFile, @@ -612,7 +636,7 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { // symbols when there are more than one targets that produce .o as their product. settings[.CLANG_COVERAGE_MAPPING_LINKER_ARGS] = "NO" if let aliases = target.moduleAliases { - settings[.SWIFT_MODULE_ALIASES] = aliases.map{ $0.key + "=" + $0.value } + settings[.SWIFT_MODULE_ALIASES] = aliases.map { $0.key + "=" + $0.value } } // Create a set of build settings that will be imparted to any target that depends on this one. @@ -632,16 +656,16 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { // Also propagate this search path to all direct and indirect clients. impartedSettings[.HEADER_SEARCH_PATHS, default: ["$(inherited)"]].append(clangTarget.includeDir.pathString) - if !fileSystem.exists(clangTarget.moduleMapPath) { + if !self.fileSystem.exists(clangTarget.moduleMapPath) { impartedSettings[.OTHER_SWIFT_FLAGS, default: ["$(inherited)"]] += ["-Xcc", "-fmodule-map-file=\(moduleMapFile)"] moduleMapFileContents = """ - module \(target.c99name) { - umbrella "\(clangTarget.includeDir.pathString)" - export * - } - """ + module \(target.c99name) { + umbrella "\(clangTarget.includeDir.pathString)" + export * + } + """ shouldImpartModuleMap = true } else { @@ -649,7 +673,9 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { shouldImpartModuleMap = false } } else if let swiftTarget = target.underlying as? SwiftTarget { - settings[.SWIFT_VERSION] = swiftTarget.swiftVersion.description + settings[.SWIFT_VERSION] = try swiftTarget + .computeEffectiveSwiftVersion(supportedSwiftVersions: self.parameters.supportedSwiftVersions) + .description // Generate ObjC compatibility header for Swift library targets. settings[.SWIFT_OBJC_INTERFACE_HEADER_DIR] = "$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)" settings[.SWIFT_OBJC_INTERFACE_HEADER_NAME] = "\(target.name)-Swift.h" @@ -657,11 +683,11 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { settings.addCommonSwiftSettings(package: self.package, target: target, parameters: self.parameters) moduleMapFileContents = """ - module \(target.c99name) { - header "\(target.name)-Swift.h" - export * - } - """ + module \(target.c99name) { + header "\(target.name)-Swift.h" + export * + } + """ shouldImpartModuleMap = true } else { @@ -686,12 +712,12 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { // radar://112671586 supress unnecessary warnings impartedSettings[.OTHER_LDFLAGS, default: ["$(inherited)"]].append("-Wl,-no_warn_duplicate_libraries") - addSources(target.sources, to: pifTarget) + self.addSources(target.sources, to: pifTarget) // Handle the target's dependencies (but don't link against them). let dependencies = try! topologicalSort(target.dependencies) { $0.packageDependencies }.sorted() for dependency in dependencies { - addDependency(to: dependency, in: pifTarget, linkProduct: false) + self.addDependency(to: dependency, in: pifTarget, linkProduct: false) } if let resourceBundle = addResourceBundle(for: target, in: pifTarget) { @@ -713,8 +739,16 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { ) let impartedBuildProperties = PIF.ImpartedBuildProperties(settings: impartedSettings) - pifTarget.addBuildConfiguration(name: "Debug", settings: debugSettings, impartedBuildProperties: impartedBuildProperties) - pifTarget.addBuildConfiguration(name: "Release", settings: releaseSettings, impartedBuildProperties: impartedBuildProperties) + pifTarget.addBuildConfiguration( + name: "Debug", + settings: debugSettings, + impartedBuildProperties: impartedBuildProperties + ) + pifTarget.addBuildConfiguration( + name: "Release", + settings: releaseSettings, + impartedBuildProperties: impartedBuildProperties + ) pifTarget.impartedBuildSettings = impartedSettings } @@ -729,10 +763,10 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { var cFlags: [String] = [] for result in try pkgConfigArgs( for: systemTarget, - pkgConfigDirectories: parameters.pkgConfigDirectories, - sdkRootPath: parameters.sdkRootPath, - fileSystem: fileSystem, - observabilityScope: observabilityScope + pkgConfigDirectories: self.parameters.pkgConfigDirectories, + sdkRootPath: self.parameters.sdkRootPath, + fileSystem: self.fileSystem, + observabilityScope: self.observabilityScope ) { if let error = result.error { self.observabilityScope.emit( @@ -746,14 +780,24 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { } impartedSettings[.OTHER_LDRFLAGS] = [] - impartedSettings[.OTHER_CFLAGS, default: ["$(inherited)"]] += ["-fmodule-map-file=\(systemTarget.moduleMapPath)"] + cFlags - impartedSettings[.OTHER_SWIFT_FLAGS, default: ["$(inherited)"]] += ["-Xcc", "-fmodule-map-file=\(systemTarget.moduleMapPath)"] + cFlags + impartedSettings[.OTHER_CFLAGS, default: ["$(inherited)"]] += + ["-fmodule-map-file=\(systemTarget.moduleMapPath)"] + cFlags + impartedSettings[.OTHER_SWIFT_FLAGS, default: ["$(inherited)"]] += + ["-Xcc", "-fmodule-map-file=\(systemTarget.moduleMapPath)"] + cFlags let impartedBuildProperties = PIF.ImpartedBuildProperties(settings: impartedSettings) // Create an aggregate PIF target (which doesn't have an actual product). let pifTarget = addAggregateTarget(guid: target.pifTargetGUID, name: target.name) - pifTarget.addBuildConfiguration(name: "Debug", settings: PIF.BuildSettings(), impartedBuildProperties: impartedBuildProperties) - pifTarget.addBuildConfiguration(name: "Release", settings: PIF.BuildSettings(), impartedBuildProperties: impartedBuildProperties) + pifTarget.addBuildConfiguration( + name: "Debug", + settings: PIF.BuildSettings(), + impartedBuildProperties: impartedBuildProperties + ) + pifTarget.addBuildConfiguration( + name: "Release", + settings: PIF.BuildSettings(), + impartedBuildProperties: impartedBuildProperties + ) pifTarget.impartedBuildSettings = impartedSettings } @@ -761,7 +805,7 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { // Create a group for the target's source files. For now we use an absolute path for it, but we should really // make it be container-relative, since it's always inside the package directory. let targetGroup = groupTree.addGroup( - path: sources.root.relative(to: package.path).pathString, + path: sources.root.relative(to: self.package.path).pathString, sourceTree: .group ) @@ -778,14 +822,14 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { ) { switch dependency { case .target(let target, let conditions): - addDependency( + self.addDependency( to: target, in: pifTarget, conditions: conditions, linkProduct: linkProduct ) case .product(let product, let conditions): - addDependency( + self.addDependency( to: product, in: pifTarget, conditions: conditions, @@ -802,17 +846,18 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { ) { // Only add the binary target as a library when we want to link against the product. if let binaryTarget = target.underlying as? BinaryTarget { - let ref = binaryGroup.addFileReference(path: binaryTarget.artifactPath.pathString) + let ref = self.binaryGroup.addFileReference(path: binaryTarget.artifactPath.pathString) pifTarget.addLibrary(ref, platformFilters: conditions.toPlatformFilters()) } else { // If this is an executable target, the dependency should be to the PIF target created from the its // product, as we don't have PIF targets corresponding to executable targets. - let targetGUID = executableTargetProductMap[target.id]?.pifTargetGUID ?? target.pifTargetGUID + let targetGUID = self.executableTargetProductMap[target.id]?.pifTargetGUID ?? target.pifTargetGUID let linkProduct = linkProduct && target.type != .systemModule && target.type != .executable pifTarget.addDependency( toTargetWithGUID: targetGUID, platformFilters: conditions.toPlatformFilters(), - linkProduct: linkProduct) + linkProduct: linkProduct + ) } } @@ -835,7 +880,7 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { } let bundleName = "\(package.manifest.displayName)_\(target.name)" // TODO: use identity instead? - let resourcesTarget = addTarget( + let resourcesTarget = self.addTarget( guid: target.pifResourceTargetGUID, name: bundleName, productType: .bundle, @@ -852,7 +897,8 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { settings[.TARGET_NAME] = bundleName settings[.PRODUCT_NAME] = bundleName settings[.PRODUCT_MODULE_NAME] = bundleName - let bundleIdentifier = "\(package.manifest.displayName).\(target.name).resources".spm_mangledToBundleIdentifier() // TODO: use identity instead? + let bundleIdentifier = "\(package.manifest.displayName).\(target.name).resources" + .spm_mangledToBundleIdentifier() // TODO: use identity instead? settings[.PRODUCT_BUNDLE_IDENTIFIER] = bundleIdentifier settings[.GENERATE_INFOPLIST_FILE] = "YES" settings[.PACKAGE_RESOURCE_TARGET_KIND] = "resource" @@ -860,7 +906,7 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { resourcesTarget.addBuildConfiguration(name: "Debug", settings: settings) resourcesTarget.addBuildConfiguration(name: "Release", settings: settings) - let coreDataFileTypes = [XCBuildFileType.xcdatamodeld, .xcdatamodel].flatMap { $0.fileTypes } + let coreDataFileTypes = [XCBuildFileType.xcdatamodeld, .xcdatamodel].flatMap(\.fileTypes) for resource in target.underlying.resources { // FIXME: Handle rules here. let resourceFile = groupTree.addFileReference( @@ -878,7 +924,10 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { } let targetGroup = groupTree.addGroup(path: "/", sourceTree: .group) - pifTarget.addResourceFile(targetGroup.addFileReference(path: "\(bundleName).bundle", sourceTree: .builtProductsDir)) + pifTarget.addResourceFile(targetGroup.addFileReference( + path: "\(bundleName).bundle", + sourceTree: .builtProductsDir + )) return bundleName } @@ -909,7 +958,8 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { for assignment in assignments { var value = assignment.value if setting == .HEADER_SEARCH_PATHS { - value = try value.map { try AbsolutePath(validating: $0, relativeTo: target.sources.root).pathString } + value = try value + .map { try AbsolutePath(validating: $0, relativeTo: target.sources.root).pathString } } if let platforms = assignment.platforms { @@ -918,10 +968,22 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { switch configuration { case .debug: debugSettings[setting, for: platform, default: ["$(inherited)"]] += value - addInferredBuildSettings(for: setting, value: value, platform: platform, configuration: .debug, settings: &debugSettings) + self.addInferredBuildSettings( + for: setting, + value: value, + platform: platform, + configuration: .debug, + settings: &debugSettings + ) case .release: releaseSettings[setting, for: platform, default: ["$(inherited)"]] += value - addInferredBuildSettings(for: setting, value: value, platform: platform, configuration: .release, settings: &releaseSettings) + self.addInferredBuildSettings( + for: setting, + value: value, + platform: platform, + configuration: .release, + settings: &releaseSettings + ) } } @@ -934,10 +996,20 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { switch configuration { case .debug: debugSettings[setting, default: ["$(inherited)"]] += value - addInferredBuildSettings(for: setting, value: value, configuration: .debug, settings: &debugSettings) + self.addInferredBuildSettings( + for: setting, + value: value, + configuration: .debug, + settings: &debugSettings + ) case .release: releaseSettings[setting, default: ["$(inherited)"]] += value - addInferredBuildSettings(for: setting, value: value, configuration: .release, settings: &releaseSettings) + self.addInferredBuildSettings( + for: setting, + value: value, + configuration: .release, + settings: &releaseSettings + ) } } @@ -989,10 +1061,18 @@ final class AggregatePIFProjectBuilder: PIFProjectBuilder { for case let project as PackagePIFProjectBuilder in projects where project.isRootPackage { for case let target as PIFTargetBuilder in project.targets { if target.productType != .unitTest { - allExcludingTestsTarget.addDependency(toTargetWithGUID: target.guid, platformFilters: [], linkProduct: false) + allExcludingTestsTarget.addDependency( + toTargetWithGUID: target.guid, + platformFilters: [], + linkProduct: false + ) } - allIncludingTestsTarget.addDependency(toTargetWithGUID: target.guid, platformFilters: [], linkProduct: false) + allIncludingTestsTarget.addDependency( + toTargetWithGUID: target.guid, + platformFilters: [], + linkProduct: false + ) } } } @@ -1021,12 +1101,12 @@ final class PIFFileReferenceBuilder: PIFReferenceBuilder { } func construct() -> PIF.Reference { - return PIF.FileReference( - guid: guid, - path: path, - sourceTree: sourceTree, - name: name, - fileType: fileType + PIF.FileReference( + guid: self.guid, + path: self.path, + sourceTree: self.sourceTree, + name: self.name, + fileType: self.fileType ) } } @@ -1044,7 +1124,7 @@ final class PIFGroupBuilder: PIFReferenceBuilder { self.path = path self.sourceTree = sourceTree self.name = name - children = [] + self.children = [] } /// Creates and appends a new Group to the list of children. The new group is returned so that it can be configured. @@ -1054,7 +1134,7 @@ final class PIFGroupBuilder: PIFReferenceBuilder { name: String? = nil ) -> PIFGroupBuilder { let group = PIFGroupBuilder(path: path, sourceTree: sourceTree, name: name) - children.append(group) + self.children.append(group) return group } @@ -1066,26 +1146,26 @@ final class PIFGroupBuilder: PIFReferenceBuilder { fileType: String? = nil ) -> PIFFileReferenceBuilder { let file = PIFFileReferenceBuilder(path: path, sourceTree: sourceTree, name: name, fileType: fileType) - children.append(file) + self.children.append(file) return file } func removeChild(_ reference: PIFReferenceBuilder) { - children.removeAll { $0 === reference } + self.children.removeAll { $0 === reference } } func construct() -> PIF.Reference { let children = self.children.enumerated().map { kvp -> PIF.Reference in let (index, builder) = kvp - builder.guid = "\(guid)::REF_\(index)" + builder.guid = "\(self.guid)::REF_\(index)" return builder.construct() } return PIF.Group( - guid: guid, - path: path, - sourceTree: sourceTree, - name: name, + guid: self.guid, + path: self.path, + sourceTree: self.sourceTree, + name: self.name, children: children ) } @@ -1115,10 +1195,15 @@ class PIFBaseTargetBuilder { public func addBuildConfiguration( name: String, settings: PIF.BuildSettings = PIF.BuildSettings(), - impartedBuildProperties: PIF.ImpartedBuildProperties = PIF.ImpartedBuildProperties(settings: PIF.BuildSettings()) + impartedBuildProperties: PIF.ImpartedBuildProperties = PIF + .ImpartedBuildProperties(settings: PIF.BuildSettings()) ) -> PIFBuildConfigurationBuilder { - let builder = PIFBuildConfigurationBuilder(name: name, settings: settings, impartedBuildProperties: impartedBuildProperties) - buildConfigurations.append(builder) + let builder = PIFBuildConfigurationBuilder( + name: name, + settings: settings, + impartedBuildProperties: impartedBuildProperties + ) + self.buildConfigurations.append(builder) return builder } @@ -1131,7 +1216,7 @@ class PIFBaseTargetBuilder { @discardableResult func addHeadersBuildPhase() -> PIFHeadersBuildPhaseBuilder { let buildPhase = PIFHeadersBuildPhaseBuilder() - buildPhases.append(buildPhase) + self.buildPhases.append(buildPhase) return buildPhase } @@ -1140,7 +1225,7 @@ class PIFBaseTargetBuilder { @discardableResult func addSourcesBuildPhase() -> PIFSourcesBuildPhaseBuilder { let buildPhase = PIFSourcesBuildPhaseBuilder() - buildPhases.append(buildPhase) + self.buildPhases.append(buildPhase) return buildPhase } @@ -1149,14 +1234,14 @@ class PIFBaseTargetBuilder { @discardableResult func addFrameworksBuildPhase() -> PIFFrameworksBuildPhaseBuilder { let buildPhase = PIFFrameworksBuildPhaseBuilder() - buildPhases.append(buildPhase) + self.buildPhases.append(buildPhase) return buildPhase } @discardableResult func addResourcesBuildPhase() -> PIFResourcesBuildPhaseBuilder { let buildPhase = PIFResourcesBuildPhaseBuilder() - buildPhases.append(buildPhase) + self.buildPhases.append(buildPhase) return buildPhase } @@ -1165,52 +1250,60 @@ class PIFBaseTargetBuilder { /// true, the receiver will also be configured to link against the product produced by the other target (this /// presumes that the product type is one that can be linked against). func addDependency(toTargetWithGUID targetGUID: String, platformFilters: [PIF.PlatformFilter], linkProduct: Bool) { - dependencies.append(.init(targetGUID: targetGUID, platformFilters: platformFilters)) + self.dependencies.append(.init(targetGUID: targetGUID, platformFilters: platformFilters)) if linkProduct { - let frameworksPhase = buildPhases.first { $0 is PIFFrameworksBuildPhaseBuilder } - ?? addFrameworksBuildPhase() + let frameworksPhase = self.buildPhases.first { $0 is PIFFrameworksBuildPhaseBuilder } + ?? self.addFrameworksBuildPhase() frameworksPhase.addBuildFile(toTargetWithGUID: targetGUID, platformFilters: platformFilters) } } /// Convenience function to add a file reference to the Headers build phase, after creating it if needed. @discardableResult - public func addHeaderFile(_ fileReference: PIFFileReferenceBuilder, headerVisibility: PIF.BuildFile.HeaderVisibility) -> PIFBuildFileBuilder { - let headerPhase = buildPhases.first { $0 is PIFHeadersBuildPhaseBuilder } ?? addHeadersBuildPhase() + public func addHeaderFile( + _ fileReference: PIFFileReferenceBuilder, + headerVisibility: PIF.BuildFile.HeaderVisibility + ) -> PIFBuildFileBuilder { + let headerPhase = self.buildPhases.first { $0 is PIFHeadersBuildPhaseBuilder } ?? self.addHeadersBuildPhase() return headerPhase.addBuildFile(to: fileReference, platformFilters: [], headerVisibility: headerVisibility) } /// Convenience function to add a file reference to the Sources build phase, after creating it if needed. @discardableResult public func addSourceFile(_ fileReference: PIFFileReferenceBuilder) -> PIFBuildFileBuilder { - let sourcesPhase = buildPhases.first { $0 is PIFSourcesBuildPhaseBuilder } ?? addSourcesBuildPhase() + let sourcesPhase = self.buildPhases.first { $0 is PIFSourcesBuildPhaseBuilder } ?? self.addSourcesBuildPhase() return sourcesPhase.addBuildFile(to: fileReference, platformFilters: []) } /// Convenience function to add a file reference to the Frameworks build phase, after creating it if needed. @discardableResult - public func addLibrary(_ fileReference: PIFFileReferenceBuilder, platformFilters: [PIF.PlatformFilter]) -> PIFBuildFileBuilder { - let frameworksPhase = buildPhases.first { $0 is PIFFrameworksBuildPhaseBuilder } ?? addFrameworksBuildPhase() + public func addLibrary( + _ fileReference: PIFFileReferenceBuilder, + platformFilters: [PIF.PlatformFilter] + ) -> PIFBuildFileBuilder { + let frameworksPhase = self.buildPhases.first { $0 is PIFFrameworksBuildPhaseBuilder } ?? self + .addFrameworksBuildPhase() return frameworksPhase.addBuildFile(to: fileReference, platformFilters: platformFilters) } @discardableResult public func addResourceFile(_ fileReference: PIFFileReferenceBuilder) -> PIFBuildFileBuilder { - let resourcesPhase = buildPhases.first { $0 is PIFResourcesBuildPhaseBuilder } ?? addResourcesBuildPhase() + let resourcesPhase = self.buildPhases.first { $0 is PIFResourcesBuildPhaseBuilder } ?? self + .addResourcesBuildPhase() return resourcesPhase.addBuildFile(to: fileReference, platformFilters: []) } fileprivate func constructBuildConfigurations() -> [PIF.BuildConfiguration] { - buildConfigurations.map { builder -> PIF.BuildConfiguration in - builder.guid = "\(guid)::BUILDCONFIG_\(builder.name)" + self.buildConfigurations.map { builder -> PIF.BuildConfiguration in + builder.guid = "\(self.guid)::BUILDCONFIG_\(builder.name)" return builder.construct() } } fileprivate func constructBuildPhases() throws -> [PIF.BuildPhase] { - try buildPhases.enumerated().map { kvp in + try self.buildPhases.enumerated().map { kvp in let (index, builder) = kvp - builder.guid = "\(guid)::BUILDPHASE_\(index)" + builder.guid = "\(self.guid)::BUILDPHASE_\(index)" return try builder.construct() } } @@ -1218,11 +1311,11 @@ class PIFBaseTargetBuilder { final class PIFAggregateTargetBuilder: PIFBaseTargetBuilder { override func construct() throws -> PIF.BaseTarget { - return PIF.AggregateTarget( + try PIF.AggregateTarget( guid: guid, name: name, buildConfigurations: constructBuildConfigurations(), - buildPhases: try self.constructBuildPhases(), + buildPhases: self.constructBuildPhases(), dependencies: dependencies, impartedBuildSettings: impartedBuildSettings ) @@ -1241,13 +1334,13 @@ final class PIFTargetBuilder: PIFBaseTargetBuilder { } override func construct() throws -> PIF.BaseTarget { - return PIF.Target( + try PIF.Target( guid: guid, name: name, - productType: productType, - productName: productName, + productType: self.productType, + productName: self.productName, buildConfigurations: constructBuildConfigurations(), - buildPhases: try self.constructBuildPhases(), + buildPhases: self.constructBuildPhases(), dependencies: dependencies, impartedBuildSettings: impartedBuildSettings ) @@ -1261,16 +1354,24 @@ class PIFBuildPhaseBuilder { var guid: PIF.GUID fileprivate init() { - buildFiles = [] + self.buildFiles = [] } /// Adds a new build file builder that refers to a file reference. /// - Parameters: /// - file: The builder for the file reference. @discardableResult - func addBuildFile(to file: PIFFileReferenceBuilder, platformFilters: [PIF.PlatformFilter], headerVisibility: PIF.BuildFile.HeaderVisibility? = nil) -> PIFBuildFileBuilder { - let builder = PIFBuildFileBuilder(file: file, platformFilters: platformFilters, headerVisibility: headerVisibility) - buildFiles.append(builder) + func addBuildFile( + to file: PIFFileReferenceBuilder, + platformFilters: [PIF.PlatformFilter], + headerVisibility: PIF.BuildFile.HeaderVisibility? = nil + ) -> PIFBuildFileBuilder { + let builder = PIFBuildFileBuilder( + file: file, + platformFilters: platformFilters, + headerVisibility: headerVisibility + ) + self.buildFiles.append(builder) return builder } @@ -1278,9 +1379,12 @@ class PIFBuildPhaseBuilder { /// - Parameters: /// - targetGUID: The GIUD referencing the target. @discardableResult - func addBuildFile(toTargetWithGUID targetGUID: PIF.GUID, platformFilters: [PIF.PlatformFilter]) -> PIFBuildFileBuilder { + func addBuildFile( + toTargetWithGUID targetGUID: PIF.GUID, + platformFilters: [PIF.PlatformFilter] + ) -> PIFBuildFileBuilder { let builder = PIFBuildFileBuilder(targetGUID: targetGUID, platformFilters: platformFilters) - buildFiles.append(builder) + self.buildFiles.append(builder) return builder } @@ -1289,9 +1393,9 @@ class PIFBuildPhaseBuilder { } fileprivate func constructBuildFiles() -> [PIF.BuildFile] { - return buildFiles.enumerated().map { kvp -> PIF.BuildFile in + self.buildFiles.enumerated().map { kvp -> PIF.BuildFile in let (index, builder) = kvp - builder.guid = "\(guid)::\(index)" + builder.guid = "\(self.guid)::\(index)" return builder.construct() } } @@ -1329,9 +1433,9 @@ final class PIFBuildFileBuilder { var pifReference: PIF.BuildFile.Reference { switch self { case .file(let builder): - return .file(guid: builder.guid) + .file(guid: builder.guid) case .target(let guid): - return .target(guid: guid) + .target(guid: guid) } } } @@ -1345,20 +1449,33 @@ final class PIFBuildFileBuilder { let headerVisibility: PIF.BuildFile.HeaderVisibility? - fileprivate init(file: PIFFileReferenceBuilder, platformFilters: [PIF.PlatformFilter], headerVisibility: PIF.BuildFile.HeaderVisibility? = nil) { - reference = .file(builder: file) + fileprivate init( + file: PIFFileReferenceBuilder, + platformFilters: [PIF.PlatformFilter], + headerVisibility: PIF.BuildFile.HeaderVisibility? = nil + ) { + self.reference = .file(builder: file) self.platformFilters = platformFilters self.headerVisibility = headerVisibility } - fileprivate init(targetGUID: PIF.GUID, platformFilters: [PIF.PlatformFilter], headerVisibility: PIF.BuildFile.HeaderVisibility? = nil) { - reference = .target(guid: targetGUID) + fileprivate init( + targetGUID: PIF.GUID, + platformFilters: [PIF.PlatformFilter], + headerVisibility: PIF.BuildFile.HeaderVisibility? = nil + ) { + self.reference = .target(guid: targetGUID) self.platformFilters = platformFilters self.headerVisibility = headerVisibility } func construct() -> PIF.BuildFile { - PIF.BuildFile(guid: guid, reference: reference.pifReference, platformFilters: platformFilters, headerVisibility: headerVisibility) + PIF.BuildFile( + guid: self.guid, + reference: self.reference.pifReference, + platformFilters: self.platformFilters, + headerVisibility: self.headerVisibility + ) } } @@ -1378,7 +1495,12 @@ final class PIFBuildConfigurationBuilder { } func construct() -> PIF.BuildConfiguration { - PIF.BuildConfiguration(guid: guid, name: name, buildSettings: settings, impartedBuildProperties: impartedBuildProperties) + PIF.BuildConfiguration( + guid: self.guid, + name: self.name, + buildSettings: self.settings, + impartedBuildProperties: self.impartedBuildProperties + ) } } @@ -1404,7 +1526,7 @@ extension ResolvedProduct { public func recursivePackageDependencies() -> [ResolvedModule.Dependency] { let initialDependencies = targets.map { ResolvedModule.Dependency.target($0, conditions: []) } return try! topologicalSort(initialDependencies) { dependency in - return dependency.packageDependencies + dependency.packageDependencies }.sorted() } } @@ -1414,20 +1536,19 @@ extension ResolvedModule { var pifResourceTargetGUID: PIF.GUID { "PACKAGE-RESOURCE:\(name)" } } -extension Array where Element == ResolvedModule.Dependency { - +extension [ResolvedModule.Dependency] { /// Sorts to get products first, sorted by name, followed by targets, sorted by name. func sorted() -> [ResolvedModule.Dependency] { - sorted { lhsDependency, rhsDependency in + self.sorted { lhsDependency, rhsDependency in switch (lhsDependency, rhsDependency) { case (.product, .target): - return true + true case (.target, .product): - return false + false case (.product(let lhsProduct, _), .product(let rhsProduct, _)): - return lhsProduct.name < rhsProduct.name + lhsProduct.name < rhsProduct.name case (.target(let lhsTarget, _), .target(let rhsTarget, _)): - return lhsTarget.name < rhsTarget.name + lhsTarget.name < rhsTarget.name } } } @@ -1435,13 +1556,13 @@ extension Array where Element == ResolvedModule.Dependency { extension ResolvedPackage { func deploymentTarget(for platform: PackageModel.Platform, usingXCTest: Bool = false) -> String? { - return self.getSupportedPlatform(for: platform, usingXCTest: usingXCTest).version.versionString + self.getSupportedPlatform(for: platform, usingXCTest: usingXCTest).version.versionString } } extension ResolvedModule { func deploymentTarget(for platform: PackageModel.Platform, usingXCTest: Bool = false) -> String? { - return self.getSupportedPlatform(for: platform, usingXCTest: usingXCTest).version.versionString + self.getSupportedPlatform(for: platform, usingXCTest: usingXCTest).version.versionString } } @@ -1451,21 +1572,46 @@ extension Target { } } +extension SwiftTarget { + func computeEffectiveSwiftVersion(supportedSwiftVersions: [SwiftLanguageVersion]) throws -> SwiftLanguageVersion { + // We have to normalize to two component strings to match the results from XCBuild w.r.t. to hashing of + // `SwiftLanguageVersion` instances. + let normalizedDeclaredVersions = Set(self.declaredSwiftVersions.compactMap { + SwiftLanguageVersion(string: "\($0.major).\($0.minor)") + }) + // If we were able to determine the list of versions supported by XCBuild, cross-reference with the package's + // Swift versions in case the preferred version isn't available. + if !supportedSwiftVersions.isEmpty, !supportedSwiftVersions.contains(self.swiftVersion) { + let declaredVersions = Array(normalizedDeclaredVersions.intersection(supportedSwiftVersions)).sorted(by: >) + if let swiftVersion = declaredVersions.first { + return swiftVersion + } else { + throw PIFGenerationError.unsupportedSwiftLanguageVersion( + targetName: self.name, + version: self.swiftVersion, + supportedVersions: supportedSwiftVersions + ) + } + } + return self.swiftVersion + } +} + extension ProductType { var targetType: Target.Kind { switch self { case .executable: - return .executable + .executable case .snippet: - return .snippet + .snippet case .test: - return .test + .test case .library: - return .library + .library case .plugin: - return .plugin + .plugin case .macro: - return .macro + .macro } } } @@ -1481,8 +1627,8 @@ private struct PIFBuildSettingAssignment { let platforms: [PIF.BuildSettings.Platform]? } -private extension BuildSettings.AssignmentTable { - var pifAssignments: [PIF.BuildSettings.MultipleValueSetting: [PIFBuildSettingAssignment]] { +extension BuildSettings.AssignmentTable { + fileprivate var pifAssignments: [PIF.BuildSettings.MultipleValueSetting: [PIFBuildSettingAssignment]] { var pifAssignments: [PIF.BuildSettings.MultipleValueSetting: [PIFBuildSettingAssignment]] = [:] for (declaration, assignments) in self.assignments { @@ -1508,7 +1654,8 @@ private extension BuildSettings.AssignmentTable { let pifAssignment = PIFBuildSettingAssignment( value: value, configurations: assignment.configurations, - platforms: assignment.pifPlatforms) + platforms: assignment.pifPlatforms + ) pifAssignments[setting, default: []].append(pifAssignment) } @@ -1518,20 +1665,20 @@ private extension BuildSettings.AssignmentTable { } } -private extension BuildSettings.Assignment { - var configurations: [BuildConfiguration] { +extension BuildSettings.Assignment { + fileprivate var configurations: [BuildConfiguration] { if let configurationCondition = conditions.lazy.compactMap(\.configurationCondition).first { - return [configurationCondition.configuration] + [configurationCondition.configuration] } else { - return BuildConfiguration.allCases + BuildConfiguration.allCases } } - var pifPlatforms: [PIF.BuildSettings.Platform]? { + fileprivate var pifPlatforms: [PIF.BuildSettings.Platform]? { if let platformsCondition = conditions.lazy.compactMap(\.platformsCondition).first { - return platformsCondition.platforms.compactMap { PIF.BuildSettings.Platform(rawValue: $0.name) } + platformsCondition.platforms.compactMap { PIF.BuildSettings.Platform(rawValue: $0.name) } } else { - return nil + nil } } } @@ -1540,8 +1687,7 @@ private extension BuildSettings.Assignment { public struct DelayedImmutable { private var _value: Value? = nil - public init() { - } + public init() {} public var wrappedValue: Value { get { @@ -1551,10 +1697,10 @@ public struct DelayedImmutable { return value } set { - if _value != nil { + if self._value != nil { fatalError("property initialized twice") } - _value = newValue + self._value = newValue } } } @@ -1562,7 +1708,7 @@ public struct DelayedImmutable { extension [PackageCondition] { func toPlatformFilters() -> [PIF.PlatformFilter] { var result: [PIF.PlatformFilter] = [] - let platformConditions = self.compactMap(\.platformsCondition).flatMap { $0.platforms } + let platformConditions = self.compactMap(\.platformsCondition).flatMap(\.platforms) for condition in platformConditions { switch condition { @@ -1604,7 +1750,6 @@ extension [PackageCondition] { default: assertionFailure("Unhandled platform condition: \(condition)") - break } } return result @@ -1612,31 +1757,30 @@ extension [PackageCondition] { } extension PIF.PlatformFilter { - /// macOS platform filters. public static let macOSFilters: [PIF.PlatformFilter] = [.init(platform: "macos")] /// Mac Catalyst platform filters. public static let macCatalystFilters: [PIF.PlatformFilter] = [ - .init(platform: "ios", environment: "maccatalyst") + .init(platform: "ios", environment: "maccatalyst"), ] /// iOS platform filters. public static let iOSFilters: [PIF.PlatformFilter] = [ .init(platform: "ios"), - .init(platform: "ios", environment: "simulator") + .init(platform: "ios", environment: "simulator"), ] /// tvOS platform filters. public static let tvOSFilters: [PIF.PlatformFilter] = [ .init(platform: "tvos"), - .init(platform: "tvos", environment: "simulator") + .init(platform: "tvos", environment: "simulator"), ] /// watchOS platform filters. public static let watchOSFilters: [PIF.PlatformFilter] = [ .init(platform: "watchos"), - .init(platform: "watchos", environment: "simulator") + .init(platform: "watchos", environment: "simulator"), ] /// DriverKit platform filters. @@ -1657,11 +1801,9 @@ extension PIF.PlatformFilter { ] /// Common Linux platform filters. - public static let linuxFilters: [PIF.PlatformFilter] = { - ["", "eabi", "gnu", "gnueabi", "gnueabihf"].map { - .init(platform: "linux", environment: $0) - } - }() + public static let linuxFilters: [PIF.PlatformFilter] = ["", "eabi", "gnu", "gnueabi", "gnueabihf"].map { + .init(platform: "linux", environment: $0) + } /// OpenBSD filters. public static let openBSDFilters: [PIF.PlatformFilter] = [ @@ -1678,12 +1820,12 @@ extension PIF.PlatformFilter { .init(platform: "xros"), .init(platform: "xros", environment: "simulator"), .init(platform: "visionos"), - .init(platform: "visionos", environment: "simulator") + .init(platform: "visionos", environment: "simulator"), ] } -private extension PIF.BuildSettings { - mutating func addCommonSwiftSettings( +extension PIF.BuildSettings { + fileprivate mutating func addCommonSwiftSettings( package: ResolvedPackage, target: ResolvedModule, parameters: PIFBuilderParameters @@ -1698,17 +1840,25 @@ private extension PIF.BuildSettings { } } -private extension PIF.BuildSettings.Platform { - static func from(platform: PackageModel.Platform) -> PIF.BuildSettings.Platform? { +extension PIF.BuildSettings.Platform { + fileprivate static func from(platform: PackageModel.Platform) -> PIF.BuildSettings.Platform? { switch platform { - case .iOS: return .iOS - case .linux: return .linux - case .macCatalyst: return .macCatalyst - case .macOS: return .macOS - case .tvOS: return .tvOS - case .watchOS: return .watchOS - case .driverKit: return .driverKit - default: return nil + case .iOS: .iOS + case .linux: .linux + case .macCatalyst: .macCatalyst + case .macOS: .macOS + case .tvOS: .tvOS + case .watchOS: .watchOS + case .driverKit: .driverKit + default: nil } } } + +public enum PIFGenerationError: Error { + case unsupportedSwiftLanguageVersion( + targetName: String, + version: SwiftLanguageVersion, + supportedVersions: [SwiftLanguageVersion] + ) +} diff --git a/Sources/XCBuildSupport/XcodeBuildSystem.swift b/Sources/XCBuildSupport/XcodeBuildSystem.swift index be247e68e85..88a7c55036b 100644 --- a/Sources/XCBuildSupport/XcodeBuildSystem.swift +++ b/Sources/XCBuildSupport/XcodeBuildSystem.swift @@ -13,16 +13,18 @@ import Basics import Dispatch import class Foundation.JSONEncoder +import class Foundation.NSArray +import class Foundation.NSDictionary import PackageGraph import PackageModel import SPMBuildCore +import func TSCBasic.memoize import protocol TSCBasic.OutputByteStream import class TSCBasic.Process import enum TSCBasic.ProcessEnv import func TSCBasic.withTemporaryFile -import func TSCBasic.memoize import enum TSCUtility.Diagnostics @@ -45,9 +47,9 @@ package final class XcodeBuildSystem: SPMBuildCore.BuildSystem { public var builtTestProducts: [BuiltTestProduct] { do { let graph = try getPackageGraph() - + var builtProducts: [BuiltTestProduct] = [] - + for package in graph.rootPackages { for product in package.products where product.type == .test { let binaryPath = try buildParameters.binaryPath(for: product) @@ -61,7 +63,7 @@ package final class XcodeBuildSystem: SPMBuildCore.BuildSystem { ) } } - + return builtProducts } catch { self.observabilityScope.emit(error) @@ -95,7 +97,10 @@ package final class XcodeBuildSystem: SPMBuildCore.BuildSystem { } else { let xcodeSelectOutput = try TSCBasic.Process.popen(args: "xcode-select", "-p").utf8Output().spm_chomp() let xcodeDirectory = try AbsolutePath(validating: xcodeSelectOutput) - xcbuildPath = try AbsolutePath(validating: "../SharedFrameworks/XCBuild.framework/Versions/A/Support/xcbuild", relativeTo: xcodeDirectory) + xcbuildPath = try AbsolutePath( + validating: "../SharedFrameworks/XCBuild.framework/Versions/A/Support/xcbuild", + relativeTo: xcodeDirectory + ) } guard fileSystem.exists(xcbuildPath) else { @@ -103,6 +108,42 @@ package final class XcodeBuildSystem: SPMBuildCore.BuildSystem { } } + private func supportedSwiftVersions() throws -> [SwiftLanguageVersion] { + for path in [ + "../../../../../Developer/Library/Xcode/Plug-ins/XCBSpecifications.ideplugin/Contents/Resources/Swift.xcspec", + "../PlugIns/XCBBuildService.bundle/Contents/PlugIns/XCBSpecifications.ideplugin/Contents/Resources/Swift.xcspec", + ] { + let swiftSpecPath = try AbsolutePath(validating: path, relativeTo: xcbuildPath.parentDirectory) + if !fileSystem.exists(swiftSpecPath) { + continue + } + + let swiftSpec = NSArray(contentsOfFile: swiftSpecPath.pathString) + let compilerSpec = swiftSpec?.compactMap { $0 as? NSDictionary }.first { + if let identifier = $0["Identifier"] as? String { + identifier == "com.apple.xcode.tools.swift.compiler" + } else { + false + } + } + let supportedSwiftVersions: [SwiftLanguageVersion] = if let versions = + compilerSpec?["SupportedLanguageVersions"] as? NSArray + { + versions.compactMap { + if let stringValue = $0 as? String { + SwiftLanguageVersion(string: stringValue) + } else { + nil + } + } + } else { + [] + } + return supportedSwiftVersions + } + return [] + } + public func build(subset: BuildSubset) throws { guard !buildParameters.shouldSkipBuilding else { return @@ -121,7 +162,7 @@ package final class XcodeBuildSystem: SPMBuildCore.BuildSystem { "--derivedDataPath", buildParameters.dataPath.pathString, "--target", - subset.pifTargetName + subset.pifTargetName, ] let buildParamsFile: AbsolutePath? @@ -154,11 +195,16 @@ package final class XcodeBuildSystem: SPMBuildCore.BuildSystem { stderrBuffer.append(contentsOf: bytes) }) - // We need to sanitize the environment we are passing to XCBuild because we could otherwise interfere with its linked dependencies e.g. when we have a custom swift-driver dynamic library in the path. + // We need to sanitize the environment we are passing to XCBuild because we could otherwise interfere with its + // linked dependencies e.g. when we have a custom swift-driver dynamic library in the path. var sanitizedEnvironment = ProcessEnv.vars sanitizedEnvironment["DYLD_LIBRARY_PATH"] = nil - let process = TSCBasic.Process(arguments: arguments, environment: sanitizedEnvironment, outputRedirection: redirection) + let process = TSCBasic.Process( + arguments: arguments, + environment: sanitizedEnvironment, + outputRedirection: redirection + ) try process.launch() let result = try process.waitUntilExit() @@ -198,28 +244,29 @@ package final class XcodeBuildSystem: SPMBuildCore.BuildSystem { var settings: [String: String] = [:] // An error with determining the override should not be fatal here. settings["CC"] = try? buildParameters.toolchain.getClangCompiler().pathString - // Always specify the path of the effective Swift compiler, which was determined in the same way as for the native build system. + // Always specify the path of the effective Swift compiler, which was determined in the same way as for the + // native build system. settings["SWIFT_EXEC"] = buildParameters.toolchain.swiftCompilerPath.pathString - settings["LIBRARY_SEARCH_PATHS"] = "$(inherited) \(try buildParameters.toolchain.toolchainLibDir.pathString)" + settings["LIBRARY_SEARCH_PATHS"] = try "$(inherited) \(buildParameters.toolchain.toolchainLibDir.pathString)" settings["OTHER_CFLAGS"] = ( ["$(inherited)"] - + buildParameters.toolchain.extraFlags.cCompilerFlags.map { $0.spm_shellEscaped() } - + buildParameters.flags.cCompilerFlags.map { $0.spm_shellEscaped() } + + buildParameters.toolchain.extraFlags.cCompilerFlags.map { $0.spm_shellEscaped() } + + buildParameters.flags.cCompilerFlags.map { $0.spm_shellEscaped() } ).joined(separator: " ") settings["OTHER_CPLUSPLUSFLAGS"] = ( ["$(inherited)"] - + buildParameters.toolchain.extraFlags.cxxCompilerFlags.map { $0.spm_shellEscaped() } - + buildParameters.flags.cxxCompilerFlags.map { $0.spm_shellEscaped() } + + buildParameters.toolchain.extraFlags.cxxCompilerFlags.map { $0.spm_shellEscaped() } + + buildParameters.flags.cxxCompilerFlags.map { $0.spm_shellEscaped() } ).joined(separator: " ") settings["OTHER_SWIFT_FLAGS"] = ( ["$(inherited)"] - + buildParameters.toolchain.extraFlags.swiftCompilerFlags.map { $0.spm_shellEscaped() } - + buildParameters.flags.swiftCompilerFlags.map { $0.spm_shellEscaped() } + + buildParameters.toolchain.extraFlags.swiftCompilerFlags.map { $0.spm_shellEscaped() } + + buildParameters.flags.swiftCompilerFlags.map { $0.spm_shellEscaped() } ).joined(separator: " ") settings["OTHER_LDFLAGS"] = ( ["$(inherited)"] - + buildParameters.toolchain.extraFlags.linkerFlags.map { $0.spm_shellEscaped() } - + buildParameters.flags.linkerFlags.map { $0.spm_shellEscaped() } + + buildParameters.toolchain.extraFlags.linkerFlags.map { $0.spm_shellEscaped() } + + buildParameters.flags.linkerFlags.map { $0.spm_shellEscaped() } ).joined(separator: " ") // Optionally also set the list of architectures to build for. @@ -242,8 +289,7 @@ package final class XcodeBuildSystem: SPMBuildCore.BuildSystem { return file } - public func cancel(deadline: DispatchTime) throws { - } + public func cancel(deadline: DispatchTime) throws {} /// Returns a new instance of `XCBuildDelegate` for a build operation. private func createBuildDelegate() -> XCBuildDelegate { @@ -265,9 +311,9 @@ package final class XcodeBuildSystem: SPMBuildCore.BuildSystem { private func getPIFBuilder() throws -> PIFBuilder { try memoize(to: &pifBuilder) { let graph = try getPackageGraph() - let pifBuilder = PIFBuilder( + let pifBuilder = try PIFBuilder( graph: graph, - parameters: .init(buildParameters), + parameters: .init(buildParameters, supportedSwiftVersions: supportedSwiftVersions()), fileSystem: self.fileSystem, observabilityScope: self.observabilityScope ) @@ -311,21 +357,22 @@ struct XCBBuildParameters: Encodable { extension BuildConfiguration { public var xcbuildName: String { switch self { - case .debug: return "Debug" - case .release: return "Release" + case .debug: "Debug" + case .release: "Release" } } } extension PIFBuilderParameters { - public init(_ buildParameters: BuildParameters) { + public init(_ buildParameters: BuildParameters, supportedSwiftVersions: [SwiftLanguageVersion]) { self.init( isPackageAccessModifierSupported: buildParameters.driverParameters.isPackageAccessModifierSupported, enableTestability: buildParameters.testingParameters.enableTestability, shouldCreateDylibForDynamicProducts: buildParameters.shouldCreateDylibForDynamicProducts, toolchainLibDir: (try? buildParameters.toolchain.toolchainLibDir) ?? .root, pkgConfigDirectories: buildParameters.pkgConfigDirectories, - sdkRootPath: buildParameters.toolchain.sdkRootPath + sdkRootPath: buildParameters.toolchain.sdkRootPath, + supportedSwiftVersions: supportedSwiftVersions ) } } @@ -334,19 +381,19 @@ extension BuildSubset { var pifTargetName: String { switch self { case .product(let name): - return PackagePIFProjectBuilder.targetName(for: name) + PackagePIFProjectBuilder.targetName(for: name) case .target(let name): - return name + name case .allExcludingTests: - return PIFBuilder.allExcludingTestsTargetName + PIFBuilder.allExcludingTestsTargetName case .allIncludingTests: - return PIFBuilder.allIncludingTestsTargetName + PIFBuilder.allIncludingTestsTargetName } } } extension Basics.Diagnostic.Severity { var isVerbose: Bool { - return self <= .info + self <= .info } } diff --git a/Tests/XCBuildSupportTests/PIFBuilderTests.swift b/Tests/XCBuildSupportTests/PIFBuilderTests.swift index 65c2fe75065..8c4cc22bc35 100644 --- a/Tests/XCBuildSupportTests/PIFBuilderTests.swift +++ b/Tests/XCBuildSupportTests/PIFBuilderTests.swift @@ -16,8 +16,8 @@ import Foundation @_spi(DontAdoptOutsideOfSwiftPMExposedForBenchmarksAndTestsOnly) import PackageGraph -@testable import PackageModel import PackageLoading +@testable import PackageModel import SPMBuildCore import SPMTestSupport @testable import XCBuildSupport @@ -35,13 +35,14 @@ class PIFBuilderTests: XCTestCase { try XCTSkipIf(true, "test is only supported on macOS") #endif // Repeat multiple times to detect non-deterministic shuffling due to sets. - for _ in 0..<10 { - let fs = InMemoryFileSystem(emptyFiles: - "/A/Sources/A1/main.swift", - "/A/Sources/A2/lib.swift", - "/A/Sources/A3/lib.swift", - "/B/Sources/B1/main.swift", - "/B/Sources/B2/lib.swift" + for _ in 0 ..< 10 { + let fs = InMemoryFileSystem( + emptyFiles: + "/A/Sources/A1/main.swift", + "/A/Sources/A2/lib.swift", + "/A/Sources/A3/lib.swift", + "/B/Sources/B1/main.swift", + "/B/Sources/B2/lib.swift" ) let observability = ObservabilitySystem.makeForTesting() @@ -59,7 +60,8 @@ class PIFBuilderTests: XCTestCase { targets: [ .init(name: "B2", dependencies: []), .init(name: "B1", dependencies: ["B2"]), - ]), + ] + ), Manifest.createRootManifest( displayName: "A", path: "/A", @@ -75,7 +77,8 @@ class PIFBuilderTests: XCTestCase { .init(name: "A1", dependencies: ["A3", "A2", .product(name: "blib", package: "B")]), .init(name: "A2", dependencies: []), .init(name: "A3", dependencies: []), - ]), + ] + ), ], observabilityScope: observability.topScope ) @@ -90,18 +93,27 @@ class PIFBuilderTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) - let projectNames = pif.workspace.projects.map({ $0.name }) + let projectNames = pif.workspace.projects.map(\.name) XCTAssertEqual(projectNames, ["A", "B", "Aggregate"]) - let projectATargetNames = pif.workspace.projects[0].targets.map({ $0.name }) - XCTAssertEqual(projectATargetNames, ["aexe_79CC9E117_PackageProduct", "alib_79D40CF5C_PackageProduct", "A2", "A3"]) + let projectATargetNames = pif.workspace.projects[0].targets.map(\.name) + XCTAssertEqual( + projectATargetNames, + ["aexe_79CC9E117_PackageProduct", "alib_79D40CF5C_PackageProduct", "A2", "A3"] + ) let targetAExeDependencies = pif.workspace.projects[0].targets[0].dependencies - XCTAssertEqual(targetAExeDependencies.map{ $0.targetGUID }, ["PACKAGE-PRODUCT:blib", "PACKAGE-TARGET:A2", "PACKAGE-TARGET:A3"]) - let projectBTargetNames = pif.workspace.projects[1].targets.map({ $0.name }) -#if ENABLE_TARGET_BASED_DEPENDENCY_RESOLUTION + XCTAssertEqual( + targetAExeDependencies.map(\.targetGUID), + ["PACKAGE-PRODUCT:blib", "PACKAGE-TARGET:A2", "PACKAGE-TARGET:A3"] + ) + let projectBTargetNames = pif.workspace.projects[1].targets.map(\.name) + #if ENABLE_TARGET_BASED_DEPENDENCY_RESOLUTION XCTAssertEqual(projectBTargetNames, ["blib_7AE74026D_PackageProduct", "B2"]) -#else - XCTAssertEqual(projectBTargetNames, ["bexe_7ADFD1428_PackageProduct", "blib_7AE74026D_PackageProduct", "B2"]) -#endif + #else + XCTAssertEqual( + projectBTargetNames, + ["bexe_7ADFD1428_PackageProduct", "blib_7AE74026D_PackageProduct", "B2"] + ) + #endif } } @@ -109,10 +121,11 @@ class PIFBuilderTests: XCTestCase { #if !os(macOS) try XCTSkipIf(true, "test is only supported on macOS") #endif - let fs = InMemoryFileSystem(emptyFiles: - "/Foo/Sources/foo/main.swift", - "/Foo/Tests/FooTests/tests.swift", - "/Bar/Sources/BarLib/lib.swift" + let fs = InMemoryFileSystem( + emptyFiles: + "/Foo/Sources/foo/main.swift", + "/Foo/Tests/FooTests/tests.swift", + "/Bar/Sources/BarLib/lib.swift" ) let observability = ObservabilitySystem.makeForTesting() @@ -131,7 +144,8 @@ class PIFBuilderTests: XCTestCase { targets: [ .init(name: "foo", dependencies: [.product(name: "BarLib", package: "Bar")]), .init(name: "FooTests", type: .test), - ]), + ] + ), Manifest.createLocalSourceControlManifest( displayName: "Bar", path: "/Bar", @@ -148,7 +162,8 @@ class PIFBuilderTests: XCTestCase { targets: [ .init(name: "BarLib"), .init(name: "BarTests", type: .test), - ]), + ] + ), ], shouldCreateMultipleTestProducts: true, observabilityScope: observability.topScope @@ -191,7 +206,10 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.ENABLE_TESTING_SEARCH_PATHS], "YES") XCTAssertEqual(settings[.ENTITLEMENTS_REQUIRED], "NO") XCTAssertEqual(settings[.GCC_OPTIMIZATION_LEVEL], "0") - XCTAssertEqual(settings[.GCC_PREPROCESSOR_DEFINITIONS], ["$(inherited)", "SWIFT_PACKAGE", "DEBUG=1"]) + XCTAssertEqual( + settings[.GCC_PREPROCESSOR_DEFINITIONS], + ["$(inherited)", "SWIFT_PACKAGE", "DEBUG=1"] + ) XCTAssertEqual(settings[.IPHONEOS_DEPLOYMENT_TARGET], "12.0") XCTAssertEqual(settings[.IPHONEOS_DEPLOYMENT_TARGET, for: .macCatalyst], "13.0") XCTAssertEqual(settings[.KEEP_PRIVATE_EXTERNS], "NO") @@ -203,7 +221,10 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.SDKROOT], "auto") XCTAssertEqual(settings[.SKIP_INSTALL], "YES") XCTAssertEqual(settings[.SUPPORTED_PLATFORMS], ["$(AVAILABLE_PLATFORMS)"]) - XCTAssertEqual(settings[.SWIFT_ACTIVE_COMPILATION_CONDITIONS], ["$(inherited)", "SWIFT_PACKAGE", "DEBUG"]) + XCTAssertEqual( + settings[.SWIFT_ACTIVE_COMPILATION_CONDITIONS], + ["$(inherited)", "SWIFT_PACKAGE", "DEBUG"] + ) XCTAssertEqual(settings[.SWIFT_INSTALL_OBJC_HEADER], "NO") XCTAssertEqual(settings[.SWIFT_OBJC_INTERFACE_HEADER_NAME], "") XCTAssertEqual(settings[.SWIFT_OPTIMIZATION_LEVEL], "-Onone") @@ -249,7 +270,10 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.SDKROOT], "auto") XCTAssertEqual(settings[.SKIP_INSTALL], "YES") XCTAssertEqual(settings[.SUPPORTED_PLATFORMS], ["$(AVAILABLE_PLATFORMS)"]) - XCTAssertEqual(settings[.SWIFT_ACTIVE_COMPILATION_CONDITIONS], ["$(inherited)", "SWIFT_PACKAGE"]) + XCTAssertEqual( + settings[.SWIFT_ACTIVE_COMPILATION_CONDITIONS], + ["$(inherited)", "SWIFT_PACKAGE"] + ) XCTAssertEqual(settings[.SWIFT_INSTALL_OBJC_HEADER], "NO") XCTAssertEqual(settings[.SWIFT_OBJC_INTERFACE_HEADER_NAME], "") XCTAssertEqual(settings[.SWIFT_OPTIMIZATION_LEVEL], "-Owholemodule") @@ -295,7 +319,10 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.ENABLE_TESTING_SEARCH_PATHS], "YES") XCTAssertEqual(settings[.ENTITLEMENTS_REQUIRED], "NO") XCTAssertEqual(settings[.GCC_OPTIMIZATION_LEVEL], "0") - XCTAssertEqual(settings[.GCC_PREPROCESSOR_DEFINITIONS], ["$(inherited)", "SWIFT_PACKAGE", "DEBUG=1"]) + XCTAssertEqual( + settings[.GCC_PREPROCESSOR_DEFINITIONS], + ["$(inherited)", "SWIFT_PACKAGE", "DEBUG=1"] + ) XCTAssertEqual(settings[.IPHONEOS_DEPLOYMENT_TARGET], "12.0") XCTAssertEqual(settings[.IPHONEOS_DEPLOYMENT_TARGET, for: .macCatalyst], "13.0") XCTAssertEqual(settings[.KEEP_PRIVATE_EXTERNS], "NO") @@ -307,7 +334,10 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.SDKROOT], "auto") XCTAssertEqual(settings[.SKIP_INSTALL], "YES") XCTAssertEqual(settings[.SUPPORTED_PLATFORMS], ["$(AVAILABLE_PLATFORMS)"]) - XCTAssertEqual(settings[.SWIFT_ACTIVE_COMPILATION_CONDITIONS], ["$(inherited)", "SWIFT_PACKAGE", "DEBUG"]) + XCTAssertEqual( + settings[.SWIFT_ACTIVE_COMPILATION_CONDITIONS], + ["$(inherited)", "SWIFT_PACKAGE", "DEBUG"] + ) XCTAssertEqual(settings[.SWIFT_INSTALL_OBJC_HEADER], "NO") XCTAssertEqual(settings[.SWIFT_OBJC_INTERFACE_HEADER_NAME], "") XCTAssertEqual(settings[.SWIFT_OPTIMIZATION_LEVEL], "-Onone") @@ -353,7 +383,10 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.SDKROOT], "auto") XCTAssertEqual(settings[.SKIP_INSTALL], "YES") XCTAssertEqual(settings[.SUPPORTED_PLATFORMS], ["$(AVAILABLE_PLATFORMS)"]) - XCTAssertEqual(settings[.SWIFT_ACTIVE_COMPILATION_CONDITIONS], ["$(inherited)", "SWIFT_PACKAGE"]) + XCTAssertEqual( + settings[.SWIFT_ACTIVE_COMPILATION_CONDITIONS], + ["$(inherited)", "SWIFT_PACKAGE"] + ) XCTAssertEqual(settings[.SWIFT_INSTALL_OBJC_HEADER], "NO") XCTAssertEqual(settings[.SWIFT_OBJC_INTERFACE_HEADER_NAME], "") XCTAssertEqual(settings[.SWIFT_OPTIMIZATION_LEVEL], "-Owholemodule") @@ -392,14 +425,15 @@ class PIFBuilderTests: XCTestCase { #if !os(macOS) try XCTSkipIf(true, "test is only supported on macOS") #endif - let fs = InMemoryFileSystem(emptyFiles: - "/Foo/Sources/foo/main.swift", - "/Foo/Sources/cfoo/main.c", - "/Foo/Sources/FooLib/lib.swift", - "/Foo/Sources/SystemLib/module.modulemap", - "/Bar/Sources/bar/main.swift", - "/Bar/Sources/cbar/main.c", - "/Bar/Sources/BarLib/lib.swift" + let fs = InMemoryFileSystem( + emptyFiles: + "/Foo/Sources/foo/main.swift", + "/Foo/Sources/cfoo/main.c", + "/Foo/Sources/FooLib/lib.swift", + "/Foo/Sources/SystemLib/module.modulemap", + "/Bar/Sources/bar/main.swift", + "/Bar/Sources/cbar/main.c", + "/Bar/Sources/BarLib/lib.swift" ) let observability = ObservabilitySystem.makeForTesting() @@ -420,14 +454,15 @@ class PIFBuilderTests: XCTestCase { "SystemLib", "cfoo", .product(name: "bar", package: "Bar"), - .product(name: "cbar", package: "Bar") + .product(name: "cbar", package: "Bar"), ]), .init(name: "cfoo"), .init(name: "SystemLib", type: .system, pkgConfig: "Foo"), .init(name: "FooLib", dependencies: [ .product(name: "BarLib", package: "Bar"), - ]) - ]), + ]), + ] + ), Manifest.createLocalSourceControlManifest( displayName: "Bar", path: "/Bar", @@ -444,13 +479,14 @@ class PIFBuilderTests: XCTestCase { .init(name: "bar", dependencies: ["BarLib"]), .init(name: "cbar"), .init(name: "BarLib"), - ]), + ] + ), ], observabilityScope: observability.topScope ) var pif: PIF.TopLevelObject! - try withCustomEnv(["PKG_CONFIG_PATH": inputsDir.pathString]) { + try withCustomEnv(["PKG_CONFIG_PATH": self.inputsDir.pathString]) { let builder = PIFBuilder( graph: graph, parameters: .mock(), @@ -477,7 +513,7 @@ class PIFBuilderTests: XCTestCase { "PACKAGE-PRODUCT:BarLib", "PACKAGE-PRODUCT:cbar", "PACKAGE-TARGET:FooLib", - "PACKAGE-TARGET:SystemLib" + "PACKAGE-TARGET:SystemLib", ]) XCTAssertEqual(target.sources, ["/Foo/Sources/foo/main.swift"]) XCTAssertEqual(target.frameworks, [ @@ -495,7 +531,10 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.DEFINES_MODULE], "YES") XCTAssertEqual(settings[.EXECUTABLE_NAME], "foo") XCTAssertEqual(settings[.INSTALL_PATH], "/usr/local/bin") - XCTAssertEqual(settings[.LD_RUNPATH_SEARCH_PATHS], ["$(inherited)", "@executable_path/../lib"]) + XCTAssertEqual( + settings[.LD_RUNPATH_SEARCH_PATHS], + ["$(inherited)", "@executable_path/../lib"] + ) XCTAssertEqual(settings[.PACKAGE_RESOURCE_TARGET_KIND], "regular") XCTAssertEqual(settings[.PRODUCT_BUNDLE_IDENTIFIER], "foo") XCTAssertEqual(settings[.PRODUCT_MODULE_NAME], "foo") @@ -505,7 +544,10 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.SUPPORTED_PLATFORMS], ["macosx", "linux"]) XCTAssertEqual(settings[.SWIFT_VERSION], "5") XCTAssertEqual(settings[.TARGET_NAME], "foo") - XCTAssertEqual(settings[.LIBRARY_SEARCH_PATHS], ["$(inherited)", "/toolchain/lib/swift/macosx"]) + XCTAssertEqual( + settings[.LIBRARY_SEARCH_PATHS], + ["$(inherited)", "/toolchain/lib/swift/macosx"] + ) } } @@ -517,7 +559,10 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.DEFINES_MODULE], "YES") XCTAssertEqual(settings[.EXECUTABLE_NAME], "foo") XCTAssertEqual(settings[.INSTALL_PATH], "/usr/local/bin") - XCTAssertEqual(settings[.LD_RUNPATH_SEARCH_PATHS], ["$(inherited)", "@executable_path/../lib"]) + XCTAssertEqual( + settings[.LD_RUNPATH_SEARCH_PATHS], + ["$(inherited)", "@executable_path/../lib"] + ) XCTAssertEqual(settings[.PACKAGE_RESOURCE_TARGET_KIND], "regular") XCTAssertEqual(settings[.PRODUCT_BUNDLE_IDENTIFIER], "foo") XCTAssertEqual(settings[.PRODUCT_MODULE_NAME], "foo") @@ -527,7 +572,10 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.SUPPORTED_PLATFORMS], ["macosx", "linux"]) XCTAssertEqual(settings[.SWIFT_VERSION], "5") XCTAssertEqual(settings[.TARGET_NAME], "foo") - XCTAssertEqual(settings[.LIBRARY_SEARCH_PATHS], ["$(inherited)", "/toolchain/lib/swift/macosx"]) + XCTAssertEqual( + settings[.LIBRARY_SEARCH_PATHS], + ["$(inherited)", "/toolchain/lib/swift/macosx"] + ) } } @@ -551,9 +599,15 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.CLANG_ENABLE_MODULES], "YES") XCTAssertEqual(settings[.DEFINES_MODULE], "YES") XCTAssertEqual(settings[.EXECUTABLE_NAME], "cfoo") - XCTAssertEqual(settings[.HEADER_SEARCH_PATHS], ["$(inherited)", "/Foo/Sources/cfoo/include"]) + XCTAssertEqual( + settings[.HEADER_SEARCH_PATHS], + ["$(inherited)", "/Foo/Sources/cfoo/include"] + ) XCTAssertEqual(settings[.INSTALL_PATH], "/usr/local/bin") - XCTAssertEqual(settings[.LD_RUNPATH_SEARCH_PATHS], ["$(inherited)", "@executable_path/../lib"]) + XCTAssertEqual( + settings[.LD_RUNPATH_SEARCH_PATHS], + ["$(inherited)", "@executable_path/../lib"] + ) XCTAssertEqual(settings[.PACKAGE_RESOURCE_TARGET_KIND], "regular") XCTAssertEqual(settings[.PRODUCT_BUNDLE_IDENTIFIER], "cfoo") XCTAssertEqual(settings[.PRODUCT_MODULE_NAME], "cfoo") @@ -562,7 +616,10 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.SKIP_INSTALL], "NO") XCTAssertEqual(settings[.SUPPORTED_PLATFORMS], ["macosx", "linux"]) XCTAssertEqual(settings[.TARGET_NAME], "cfoo") - XCTAssertEqual(settings[.LIBRARY_SEARCH_PATHS], ["$(inherited)", "/toolchain/lib/swift/macosx"]) + XCTAssertEqual( + settings[.LIBRARY_SEARCH_PATHS], + ["$(inherited)", "/toolchain/lib/swift/macosx"] + ) } } @@ -573,9 +630,15 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.CLANG_ENABLE_MODULES], "YES") XCTAssertEqual(settings[.DEFINES_MODULE], "YES") XCTAssertEqual(settings[.EXECUTABLE_NAME], "cfoo") - XCTAssertEqual(settings[.HEADER_SEARCH_PATHS], ["$(inherited)", "/Foo/Sources/cfoo/include"]) + XCTAssertEqual( + settings[.HEADER_SEARCH_PATHS], + ["$(inherited)", "/Foo/Sources/cfoo/include"] + ) XCTAssertEqual(settings[.INSTALL_PATH], "/usr/local/bin") - XCTAssertEqual(settings[.LD_RUNPATH_SEARCH_PATHS], ["$(inherited)", "@executable_path/../lib"]) + XCTAssertEqual( + settings[.LD_RUNPATH_SEARCH_PATHS], + ["$(inherited)", "@executable_path/../lib"] + ) XCTAssertEqual(settings[.PACKAGE_RESOURCE_TARGET_KIND], "regular") XCTAssertEqual(settings[.PRODUCT_BUNDLE_IDENTIFIER], "cfoo") XCTAssertEqual(settings[.PRODUCT_MODULE_NAME], "cfoo") @@ -584,7 +647,10 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.SKIP_INSTALL], "NO") XCTAssertEqual(settings[.SUPPORTED_PLATFORMS], ["macosx", "linux"]) XCTAssertEqual(settings[.TARGET_NAME], "cfoo") - XCTAssertEqual(settings[.LIBRARY_SEARCH_PATHS], ["$(inherited)", "/toolchain/lib/swift/macosx"]) + XCTAssertEqual( + settings[.LIBRARY_SEARCH_PATHS], + ["$(inherited)", "/toolchain/lib/swift/macosx"] + ) } } @@ -619,7 +685,10 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.SUPPORTED_PLATFORMS], ["macosx", "linux"]) XCTAssertEqual(settings[.SWIFT_VERSION], "4.2") XCTAssertEqual(settings[.TARGET_NAME], "bar") - XCTAssertEqual(settings[.LIBRARY_SEARCH_PATHS], ["$(inherited)", "/toolchain/lib/swift/macosx"]) + XCTAssertEqual( + settings[.LIBRARY_SEARCH_PATHS], + ["$(inherited)", "/toolchain/lib/swift/macosx"] + ) } } @@ -638,7 +707,10 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.SUPPORTED_PLATFORMS], ["macosx", "linux"]) XCTAssertEqual(settings[.SWIFT_VERSION], "4.2") XCTAssertEqual(settings[.TARGET_NAME], "bar") - XCTAssertEqual(settings[.LIBRARY_SEARCH_PATHS], ["$(inherited)", "/toolchain/lib/swift/macosx"]) + XCTAssertEqual( + settings[.LIBRARY_SEARCH_PATHS], + ["$(inherited)", "/toolchain/lib/swift/macosx"] + ) } } @@ -664,7 +736,10 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.DEFINES_MODULE], "YES") XCTAssertEqual(settings[.EXECUTABLE_NAME], "cbar") XCTAssertEqual(settings[.GCC_C_LANGUAGE_STANDARD], "c11") - XCTAssertEqual(settings[.HEADER_SEARCH_PATHS], ["$(inherited)", "/Bar/Sources/cbar/include"]) + XCTAssertEqual( + settings[.HEADER_SEARCH_PATHS], + ["$(inherited)", "/Bar/Sources/cbar/include"] + ) XCTAssertEqual(settings[.PACKAGE_RESOURCE_TARGET_KIND], "regular") XCTAssertEqual(settings[.PRODUCT_BUNDLE_IDENTIFIER], "cbar") XCTAssertEqual(settings[.PRODUCT_MODULE_NAME], "cbar") @@ -672,7 +747,10 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.SDKROOT], "macosx") XCTAssertEqual(settings[.SUPPORTED_PLATFORMS], ["macosx", "linux"]) XCTAssertEqual(settings[.TARGET_NAME], "cbar") - XCTAssertEqual(settings[.LIBRARY_SEARCH_PATHS], ["$(inherited)", "/toolchain/lib/swift/macosx"]) + XCTAssertEqual( + settings[.LIBRARY_SEARCH_PATHS], + ["$(inherited)", "/toolchain/lib/swift/macosx"] + ) } } @@ -685,7 +763,10 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.DEFINES_MODULE], "YES") XCTAssertEqual(settings[.EXECUTABLE_NAME], "cbar") XCTAssertEqual(settings[.GCC_C_LANGUAGE_STANDARD], "c11") - XCTAssertEqual(settings[.HEADER_SEARCH_PATHS], ["$(inherited)", "/Bar/Sources/cbar/include"]) + XCTAssertEqual( + settings[.HEADER_SEARCH_PATHS], + ["$(inherited)", "/Bar/Sources/cbar/include"] + ) XCTAssertEqual(settings[.PACKAGE_RESOURCE_TARGET_KIND], "regular") XCTAssertEqual(settings[.PRODUCT_BUNDLE_IDENTIFIER], "cbar") XCTAssertEqual(settings[.PRODUCT_MODULE_NAME], "cbar") @@ -693,7 +774,10 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.SDKROOT], "macosx") XCTAssertEqual(settings[.SUPPORTED_PLATFORMS], ["macosx", "linux"]) XCTAssertEqual(settings[.TARGET_NAME], "cbar") - XCTAssertEqual(settings[.LIBRARY_SEARCH_PATHS], ["$(inherited)", "/toolchain/lib/swift/macosx"]) + XCTAssertEqual( + settings[.LIBRARY_SEARCH_PATHS], + ["$(inherited)", "/toolchain/lib/swift/macosx"] + ) } } @@ -705,19 +789,20 @@ class PIFBuilderTests: XCTestCase { func testTestProducts() throws { #if !os(macOS) - try XCTSkipIf(true, "test is only supported on macOS") + try XCTSkipIf(true, "test is only supported on macOS") #endif - let fs = InMemoryFileSystem(emptyFiles: - "/Foo/Sources/FooTests/FooTests.swift", - "/Foo/Sources/CFooTests/CFooTests.m", - "/Foo/Sources/foo/main.swift", - "/Foo/Sources/FooLib/lib.swift", - "/Foo/Sources/SystemLib/module.modulemap", - "/Bar/Sources/bar/main.swift", - "/Bar/Sources/BarTests/BarTests.swift", - "/Bar/Sources/CBarTests/CBarTests.m", - "/Bar/Sources/BarLib/lib.swift", - inputsDir.appending("Foo.pc").pathString + let fs = InMemoryFileSystem( + emptyFiles: + "/Foo/Sources/FooTests/FooTests.swift", + "/Foo/Sources/CFooTests/CFooTests.m", + "/Foo/Sources/foo/main.swift", + "/Foo/Sources/FooLib/lib.swift", + "/Foo/Sources/SystemLib/module.modulemap", + "/Bar/Sources/bar/main.swift", + "/Bar/Sources/BarTests/BarTests.swift", + "/Bar/Sources/CBarTests/CBarTests.m", + "/Bar/Sources/BarLib/lib.swift", + inputsDir.appending("Foo.pc").pathString ) let observability = ObservabilitySystem.makeForTesting() @@ -744,8 +829,9 @@ class PIFBuilderTests: XCTestCase { .init(name: "SystemLib", type: .system, pkgConfig: "Foo"), .init(name: "FooLib", dependencies: [ .product(name: "BarLib", package: "Bar"), - ]) - ]), + ]), + ] + ), Manifest.createLocalSourceControlManifest( displayName: "Bar", path: "/Bar", @@ -762,14 +848,15 @@ class PIFBuilderTests: XCTestCase { .init(name: "BarTests", dependencies: ["BarLib"], type: .test), .init(name: "CBarTests", type: .test), .init(name: "BarLib"), - ]), + ] + ), ], shouldCreateMultipleTestProducts: true, observabilityScope: observability.topScope ) var pif: PIF.TopLevelObject! - try withCustomEnv(["PKG_CONFIG_PATH": inputsDir.pathString]) { + try withCustomEnv(["PKG_CONFIG_PATH": self.inputsDir.pathString]) { let builder = PIFBuilder( graph: graph, parameters: .mock(), @@ -792,7 +879,7 @@ class PIFBuilderTests: XCTestCase { "PACKAGE-PRODUCT:bar", "PACKAGE-PRODUCT:BarLib", "PACKAGE-TARGET:FooLib", - "PACKAGE-TARGET:SystemLib" + "PACKAGE-TARGET:SystemLib", ]) XCTAssertEqual(target.sources, ["/Foo/Sources/FooTests/FooTests.swift"]) XCTAssertEqual(target.frameworks, [ @@ -812,11 +899,11 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.LD_RUNPATH_SEARCH_PATHS], [ "$(inherited)", "@loader_path/Frameworks", - "@loader_path/../Frameworks" + "@loader_path/../Frameworks", ]) XCTAssertEqual(settings[.LIBRARY_SEARCH_PATHS], [ "$(inherited)", - "/toolchain/lib/swift/macosx" + "/toolchain/lib/swift/macosx", ]) XCTAssertEqual(settings[.PACKAGE_RESOURCE_TARGET_KIND], "regular") XCTAssertEqual(settings[.PRODUCT_BUNDLE_IDENTIFIER], "FooTests") @@ -824,11 +911,28 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.PRODUCT_NAME], "FooTests") XCTAssertEqual(settings[.SWIFT_VERSION], "5") XCTAssertEqual(settings[.TARGET_NAME], "FooTests") - XCTAssertEqual(settings[.WATCHOS_DEPLOYMENT_TARGET], MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .watchOS).versionString) - XCTAssertEqual(settings[.IPHONEOS_DEPLOYMENT_TARGET], MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .iOS).versionString) - XCTAssertEqual(settings[.TVOS_DEPLOYMENT_TARGET], MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .tvOS).versionString) - XCTAssertEqual(settings[.MACOSX_DEPLOYMENT_TARGET], MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .macOS).versionString) - XCTAssertEqual(settings[.XROS_DEPLOYMENT_TARGET], MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .visionOS).versionString) + XCTAssertEqual( + settings[.WATCHOS_DEPLOYMENT_TARGET], + MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .watchOS) + .versionString + ) + XCTAssertEqual( + settings[.IPHONEOS_DEPLOYMENT_TARGET], + MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .iOS).versionString + ) + XCTAssertEqual( + settings[.TVOS_DEPLOYMENT_TARGET], + MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .tvOS).versionString + ) + XCTAssertEqual( + settings[.MACOSX_DEPLOYMENT_TARGET], + MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .macOS).versionString + ) + XCTAssertEqual( + settings[.XROS_DEPLOYMENT_TARGET], + MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .visionOS) + .versionString + ) } } @@ -843,11 +947,11 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.LD_RUNPATH_SEARCH_PATHS], [ "$(inherited)", "@loader_path/Frameworks", - "@loader_path/../Frameworks" + "@loader_path/../Frameworks", ]) XCTAssertEqual(settings[.LIBRARY_SEARCH_PATHS], [ "$(inherited)", - "/toolchain/lib/swift/macosx" + "/toolchain/lib/swift/macosx", ]) XCTAssertEqual(settings[.PACKAGE_RESOURCE_TARGET_KIND], "regular") XCTAssertEqual(settings[.PRODUCT_BUNDLE_IDENTIFIER], "FooTests") @@ -855,11 +959,28 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.PRODUCT_NAME], "FooTests") XCTAssertEqual(settings[.SWIFT_VERSION], "5") XCTAssertEqual(settings[.TARGET_NAME], "FooTests") - XCTAssertEqual(settings[.WATCHOS_DEPLOYMENT_TARGET], MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .watchOS).versionString) - XCTAssertEqual(settings[.IPHONEOS_DEPLOYMENT_TARGET], MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .iOS).versionString) - XCTAssertEqual(settings[.TVOS_DEPLOYMENT_TARGET], MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .tvOS).versionString) - XCTAssertEqual(settings[.MACOSX_DEPLOYMENT_TARGET], MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .macOS).versionString) - XCTAssertEqual(settings[.XROS_DEPLOYMENT_TARGET], MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .visionOS).versionString) + XCTAssertEqual( + settings[.WATCHOS_DEPLOYMENT_TARGET], + MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .watchOS) + .versionString + ) + XCTAssertEqual( + settings[.IPHONEOS_DEPLOYMENT_TARGET], + MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .iOS).versionString + ) + XCTAssertEqual( + settings[.TVOS_DEPLOYMENT_TARGET], + MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .tvOS).versionString + ) + XCTAssertEqual( + settings[.MACOSX_DEPLOYMENT_TARGET], + MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .macOS).versionString + ) + XCTAssertEqual( + settings[.XROS_DEPLOYMENT_TARGET], + MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .visionOS) + .versionString + ) } } @@ -884,27 +1005,44 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.GENERATE_INFOPLIST_FILE], "YES") XCTAssertEqual(settings[.HEADER_SEARCH_PATHS], [ "$(inherited)", - "/Foo/Sources/CFooTests/include" + "/Foo/Sources/CFooTests/include", ]) XCTAssertEqual(settings[.LD_RUNPATH_SEARCH_PATHS], [ "$(inherited)", "@loader_path/Frameworks", - "@loader_path/../Frameworks" + "@loader_path/../Frameworks", ]) XCTAssertEqual(settings[.LIBRARY_SEARCH_PATHS], [ "$(inherited)", - "/toolchain/lib/swift/macosx" + "/toolchain/lib/swift/macosx", ]) XCTAssertEqual(settings[.PACKAGE_RESOURCE_TARGET_KIND], "regular") XCTAssertEqual(settings[.PRODUCT_BUNDLE_IDENTIFIER], "CFooTests") XCTAssertEqual(settings[.PRODUCT_MODULE_NAME], "CFooTests") XCTAssertEqual(settings[.PRODUCT_NAME], "CFooTests") XCTAssertEqual(settings[.TARGET_NAME], "CFooTests") - XCTAssertEqual(settings[.WATCHOS_DEPLOYMENT_TARGET], MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .watchOS).versionString) - XCTAssertEqual(settings[.IPHONEOS_DEPLOYMENT_TARGET], MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .iOS).versionString) - XCTAssertEqual(settings[.TVOS_DEPLOYMENT_TARGET], MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .tvOS).versionString) - XCTAssertEqual(settings[.MACOSX_DEPLOYMENT_TARGET], MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .macOS).versionString) - XCTAssertEqual(settings[.XROS_DEPLOYMENT_TARGET], MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .visionOS).versionString) + XCTAssertEqual( + settings[.WATCHOS_DEPLOYMENT_TARGET], + MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .watchOS) + .versionString + ) + XCTAssertEqual( + settings[.IPHONEOS_DEPLOYMENT_TARGET], + MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .iOS).versionString + ) + XCTAssertEqual( + settings[.TVOS_DEPLOYMENT_TARGET], + MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .tvOS).versionString + ) + XCTAssertEqual( + settings[.MACOSX_DEPLOYMENT_TARGET], + MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .macOS).versionString + ) + XCTAssertEqual( + settings[.XROS_DEPLOYMENT_TARGET], + MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .visionOS) + .versionString + ) } } @@ -918,27 +1056,44 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.GENERATE_INFOPLIST_FILE], "YES") XCTAssertEqual(settings[.HEADER_SEARCH_PATHS], [ "$(inherited)", - "/Foo/Sources/CFooTests/include" + "/Foo/Sources/CFooTests/include", ]) XCTAssertEqual(settings[.LD_RUNPATH_SEARCH_PATHS], [ "$(inherited)", "@loader_path/Frameworks", - "@loader_path/../Frameworks" + "@loader_path/../Frameworks", ]) XCTAssertEqual(settings[.LIBRARY_SEARCH_PATHS], [ "$(inherited)", - "/toolchain/lib/swift/macosx" + "/toolchain/lib/swift/macosx", ]) XCTAssertEqual(settings[.PACKAGE_RESOURCE_TARGET_KIND], "regular") XCTAssertEqual(settings[.PRODUCT_BUNDLE_IDENTIFIER], "CFooTests") XCTAssertEqual(settings[.PRODUCT_MODULE_NAME], "CFooTests") XCTAssertEqual(settings[.PRODUCT_NAME], "CFooTests") XCTAssertEqual(settings[.TARGET_NAME], "CFooTests") - XCTAssertEqual(settings[.WATCHOS_DEPLOYMENT_TARGET], MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .watchOS).versionString) - XCTAssertEqual(settings[.IPHONEOS_DEPLOYMENT_TARGET], MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .iOS).versionString) - XCTAssertEqual(settings[.TVOS_DEPLOYMENT_TARGET], MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .tvOS).versionString) - XCTAssertEqual(settings[.MACOSX_DEPLOYMENT_TARGET], MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .macOS).versionString) - XCTAssertEqual(settings[.XROS_DEPLOYMENT_TARGET], MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .visionOS).versionString) + XCTAssertEqual( + settings[.WATCHOS_DEPLOYMENT_TARGET], + MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .watchOS) + .versionString + ) + XCTAssertEqual( + settings[.IPHONEOS_DEPLOYMENT_TARGET], + MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .iOS).versionString + ) + XCTAssertEqual( + settings[.TVOS_DEPLOYMENT_TARGET], + MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .tvOS).versionString + ) + XCTAssertEqual( + settings[.MACOSX_DEPLOYMENT_TARGET], + MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .macOS).versionString + ) + XCTAssertEqual( + settings[.XROS_DEPLOYMENT_TARGET], + MinimumDeploymentTarget.computeXCTestMinimumDeploymentTarget(for: .visionOS) + .versionString + ) } } @@ -957,11 +1112,12 @@ class PIFBuilderTests: XCTestCase { #if !os(macOS) try XCTSkipIf(true, "test is only supported on macOS") #endif - let fs = InMemoryFileSystem(emptyFiles: - "/Foo/Sources/FooLib1/lib.swift", - "/Foo/Sources/FooLib2/lib.swift", - "/Foo/Sources/SystemLib/module.modulemap", - "/Bar/Sources/BarLib/lib.swift" + let fs = InMemoryFileSystem( + emptyFiles: + "/Foo/Sources/FooLib1/lib.swift", + "/Foo/Sources/FooLib2/lib.swift", + "/Foo/Sources/SystemLib/module.modulemap", + "/Bar/Sources/BarLib/lib.swift" ) let observability = ObservabilitySystem.makeForTesting() @@ -986,7 +1142,8 @@ class PIFBuilderTests: XCTestCase { .product(name: "BarLib", package: "Bar"), ]), .init(name: "SystemLib", type: .system, pkgConfig: "Foo"), - ]), + ] + ), Manifest.createLocalSourceControlManifest( displayName: "Bar", path: "/Bar", @@ -999,13 +1156,14 @@ class PIFBuilderTests: XCTestCase { ], targets: [ .init(name: "BarLib"), - ]), + ] + ), ], observabilityScope: observability.topScope ) var pif: PIF.TopLevelObject! - try withCustomEnv(["PKG_CONFIG_PATH": inputsDir.pathString]) { + try withCustomEnv(["PKG_CONFIG_PATH": self.inputsDir.pathString]) { let builder = PIFBuilder( graph: graph, parameters: .mock(), @@ -1051,7 +1209,7 @@ class PIFBuilderTests: XCTestCase { } } - target.checkAllImpartedBuildSettings { settings in + target.checkAllImpartedBuildSettings { _ in // No imparted build settings. } } @@ -1121,7 +1279,10 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.SKIP_INSTALL], "NO") XCTAssertEqual(settings[.TARGET_BUILD_DIR], "$(TARGET_BUILD_DIR)/PackageFrameworks") XCTAssertEqual(settings[.TARGET_NAME], "BarLib") - XCTAssertEqual(settings[.LIBRARY_SEARCH_PATHS], ["$(inherited)", "/toolchain/lib/swift/macosx"]) + XCTAssertEqual( + settings[.LIBRARY_SEARCH_PATHS], + ["$(inherited)", "/toolchain/lib/swift/macosx"] + ) } } @@ -1145,7 +1306,10 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.TARGET_BUILD_DIR], "$(TARGET_BUILD_DIR)/PackageFrameworks") XCTAssertEqual(settings[.TARGET_NAME], "BarLib") XCTAssertEqual(settings[.USES_SWIFTPM_UNSAFE_FLAGS], "NO") - XCTAssertEqual(settings[.LIBRARY_SEARCH_PATHS], ["$(inherited)", "/toolchain/lib/swift/macosx"]) + XCTAssertEqual( + settings[.LIBRARY_SEARCH_PATHS], + ["$(inherited)", "/toolchain/lib/swift/macosx"] + ) } } @@ -1159,11 +1323,12 @@ class PIFBuilderTests: XCTestCase { #if !os(macOS) try XCTSkipIf(true, "test is only supported on macOS") #endif - let fs = InMemoryFileSystem(emptyFiles: - "/Foo/Sources/FooLib1/lib.swift", - "/Foo/Sources/FooLib2/lib.cpp", - "/Foo/Sources/SystemLib/module.modulemap", - "/Bar/Sources/BarLib/lib.c" + let fs = InMemoryFileSystem( + emptyFiles: + "/Foo/Sources/FooLib1/lib.swift", + "/Foo/Sources/FooLib2/lib.cpp", + "/Foo/Sources/SystemLib/module.modulemap", + "/Bar/Sources/BarLib/lib.c" ) let observability = ObservabilitySystem.makeForTesting() @@ -1185,7 +1350,8 @@ class PIFBuilderTests: XCTestCase { .product(name: "BarLib", package: "Bar"), ]), .init(name: "SystemLib", type: .system, pkgConfig: "Foo"), - ]), + ] + ), Manifest.createLocalSourceControlManifest( displayName: "Bar", path: "/Bar", @@ -1197,13 +1363,14 @@ class PIFBuilderTests: XCTestCase { ], targets: [ .init(name: "BarLib"), - ]), + ] + ), ], observabilityScope: observability.topScope ) var pif: PIF.TopLevelObject! - try withCustomEnv(["PKG_CONFIG_PATH": inputsDir.pathString]) { + try withCustomEnv(["PKG_CONFIG_PATH": self.inputsDir.pathString]) { let builder = PIFBuilder( graph: graph, parameters: .mock(), @@ -1240,17 +1407,23 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.GENERATE_MASTER_OBJECT_FILE], "NO") XCTAssertEqual(settings[.MACH_O_TYPE], "mh_object") XCTAssertEqual(settings[.MODULEMAP_FILE_CONTENTS], """ - module FooLib1 { - header "FooLib1-Swift.h" - export * - } - """) - XCTAssertEqual(settings[.MODULEMAP_PATH], "$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)/FooLib1.modulemap") + module FooLib1 { + header "FooLib1-Swift.h" + export * + } + """) + XCTAssertEqual( + settings[.MODULEMAP_PATH], + "$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)/FooLib1.modulemap" + ) XCTAssertEqual(settings[.PACKAGE_RESOURCE_TARGET_KIND], "regular") XCTAssertEqual(settings[.PRODUCT_BUNDLE_IDENTIFIER], "FooLib1") XCTAssertEqual(settings[.PRODUCT_MODULE_NAME], "FooLib1") XCTAssertEqual(settings[.PRODUCT_NAME], "FooLib1.o") - XCTAssertEqual(settings[.SWIFT_OBJC_INTERFACE_HEADER_DIR], "$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)") + XCTAssertEqual( + settings[.SWIFT_OBJC_INTERFACE_HEADER_DIR], + "$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)" + ) XCTAssertEqual(settings[.SWIFT_OBJC_INTERFACE_HEADER_NAME], "FooLib1-Swift.h") XCTAssertEqual(settings[.SWIFT_VERSION], "5") XCTAssertEqual(settings[.TARGET_NAME], "FooLib1") @@ -1268,17 +1441,23 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.GENERATE_MASTER_OBJECT_FILE], "NO") XCTAssertEqual(settings[.MACH_O_TYPE], "mh_object") XCTAssertEqual(settings[.MODULEMAP_FILE_CONTENTS], """ - module FooLib1 { - header "FooLib1-Swift.h" - export * - } - """) - XCTAssertEqual(settings[.MODULEMAP_PATH], "$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)/FooLib1.modulemap") + module FooLib1 { + header "FooLib1-Swift.h" + export * + } + """) + XCTAssertEqual( + settings[.MODULEMAP_PATH], + "$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)/FooLib1.modulemap" + ) XCTAssertEqual(settings[.PACKAGE_RESOURCE_TARGET_KIND], "regular") XCTAssertEqual(settings[.PRODUCT_BUNDLE_IDENTIFIER], "FooLib1") XCTAssertEqual(settings[.PRODUCT_MODULE_NAME], "FooLib1") XCTAssertEqual(settings[.PRODUCT_NAME], "FooLib1.o") - XCTAssertEqual(settings[.SWIFT_OBJC_INTERFACE_HEADER_DIR], "$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)") + XCTAssertEqual( + settings[.SWIFT_OBJC_INTERFACE_HEADER_DIR], + "$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)" + ) XCTAssertEqual(settings[.SWIFT_OBJC_INTERFACE_HEADER_NAME], "FooLib1-Swift.h") XCTAssertEqual(settings[.SWIFT_VERSION], "5") XCTAssertEqual(settings[.TARGET_NAME], "FooLib1") @@ -1288,7 +1467,7 @@ class PIFBuilderTests: XCTestCase { target.checkAllImpartedBuildSettings { settings in XCTAssertEqual(settings[.OTHER_CFLAGS], [ "$(inherited)", - "-fmodule-map-file=$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)/FooLib1.modulemap" + "-fmodule-map-file=$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)/FooLib1.modulemap", ]) XCTAssertEqual(settings[.OTHER_LDRFLAGS], []) XCTAssertEqual(settings[.OTHER_LDFLAGS], ["$(inherited)", "-Wl,-no_warn_duplicate_libraries"]) @@ -1313,15 +1492,21 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.DEFINES_MODULE], "YES") XCTAssertEqual(settings[.EXECUTABLE_NAME], "FooLib2.o") XCTAssertEqual(settings[.GENERATE_MASTER_OBJECT_FILE], "NO") - XCTAssertEqual(settings[.HEADER_SEARCH_PATHS], ["$(inherited)", "/Foo/Sources/FooLib2/include"]) + XCTAssertEqual( + settings[.HEADER_SEARCH_PATHS], + ["$(inherited)", "/Foo/Sources/FooLib2/include"] + ) XCTAssertEqual(settings[.MACH_O_TYPE], "mh_object") XCTAssertEqual(settings[.MODULEMAP_FILE_CONTENTS], """ - module FooLib2 { - umbrella "/Foo/Sources/FooLib2/include" - export * - } - """) - XCTAssertEqual(settings[.MODULEMAP_PATH], "$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)/FooLib2.modulemap") + module FooLib2 { + umbrella "/Foo/Sources/FooLib2/include" + export * + } + """) + XCTAssertEqual( + settings[.MODULEMAP_PATH], + "$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)/FooLib2.modulemap" + ) XCTAssertEqual(settings[.PACKAGE_RESOURCE_TARGET_KIND], "regular") XCTAssertEqual(settings[.PRODUCT_BUNDLE_IDENTIFIER], "FooLib2") XCTAssertEqual(settings[.PRODUCT_MODULE_NAME], "FooLib2") @@ -1340,15 +1525,21 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.DEFINES_MODULE], "YES") XCTAssertEqual(settings[.EXECUTABLE_NAME], "FooLib2.o") XCTAssertEqual(settings[.GENERATE_MASTER_OBJECT_FILE], "NO") - XCTAssertEqual(settings[.HEADER_SEARCH_PATHS], ["$(inherited)", "/Foo/Sources/FooLib2/include"]) + XCTAssertEqual( + settings[.HEADER_SEARCH_PATHS], + ["$(inherited)", "/Foo/Sources/FooLib2/include"] + ) XCTAssertEqual(settings[.MACH_O_TYPE], "mh_object") XCTAssertEqual(settings[.MODULEMAP_FILE_CONTENTS], """ - module FooLib2 { - umbrella "/Foo/Sources/FooLib2/include" - export * - } - """) - XCTAssertEqual(settings[.MODULEMAP_PATH], "$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)/FooLib2.modulemap") + module FooLib2 { + umbrella "/Foo/Sources/FooLib2/include" + export * + } + """) + XCTAssertEqual( + settings[.MODULEMAP_PATH], + "$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)/FooLib2.modulemap" + ) XCTAssertEqual(settings[.PACKAGE_RESOURCE_TARGET_KIND], "regular") XCTAssertEqual(settings[.PRODUCT_BUNDLE_IDENTIFIER], "FooLib2") XCTAssertEqual(settings[.PRODUCT_MODULE_NAME], "FooLib2") @@ -1361,13 +1552,17 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.HEADER_SEARCH_PATHS], ["$(inherited)", "/Foo/Sources/FooLib2/include"]) XCTAssertEqual(settings[.OTHER_CFLAGS], [ "$(inherited)", - "-fmodule-map-file=$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)/FooLib2.modulemap" + "-fmodule-map-file=$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)/FooLib2.modulemap", ]) XCTAssertEqual(settings[.OTHER_LDRFLAGS], []) - XCTAssertEqual(settings[.OTHER_LDFLAGS], ["$(inherited)", "-lc++", "-Wl,-no_warn_duplicate_libraries"]) + XCTAssertEqual( + settings[.OTHER_LDFLAGS], + ["$(inherited)", "-lc++", "-Wl,-no_warn_duplicate_libraries"] + ) XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS], [ "$(inherited)", - "-Xcc", "-fmodule-map-file=$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)/FooLib2.modulemap" + "-Xcc", + "-fmodule-map-file=$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)/FooLib2.modulemap", ]) } } @@ -1392,15 +1587,21 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.EXECUTABLE_NAME], "BarLib.o") XCTAssertEqual(settings[.GCC_C_LANGUAGE_STANDARD], "c11") XCTAssertEqual(settings[.GENERATE_MASTER_OBJECT_FILE], "NO") - XCTAssertEqual(settings[.HEADER_SEARCH_PATHS], ["$(inherited)", "/Bar/Sources/BarLib/include"]) + XCTAssertEqual( + settings[.HEADER_SEARCH_PATHS], + ["$(inherited)", "/Bar/Sources/BarLib/include"] + ) XCTAssertEqual(settings[.MACH_O_TYPE], "mh_object") XCTAssertEqual(settings[.MODULEMAP_FILE_CONTENTS], """ - module BarLib { - umbrella "/Bar/Sources/BarLib/include" - export * - } - """) - XCTAssertEqual(settings[.MODULEMAP_PATH], "$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)/BarLib.modulemap") + module BarLib { + umbrella "/Bar/Sources/BarLib/include" + export * + } + """) + XCTAssertEqual( + settings[.MODULEMAP_PATH], + "$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)/BarLib.modulemap" + ) XCTAssertEqual(settings[.PACKAGE_RESOURCE_TARGET_KIND], "regular") XCTAssertEqual(settings[.PRODUCT_BUNDLE_IDENTIFIER], "BarLib") XCTAssertEqual(settings[.PRODUCT_MODULE_NAME], "BarLib") @@ -1419,15 +1620,21 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.EXECUTABLE_NAME], "BarLib.o") XCTAssertEqual(settings[.GCC_C_LANGUAGE_STANDARD], "c11") XCTAssertEqual(settings[.GENERATE_MASTER_OBJECT_FILE], "NO") - XCTAssertEqual(settings[.HEADER_SEARCH_PATHS], ["$(inherited)", "/Bar/Sources/BarLib/include"]) + XCTAssertEqual( + settings[.HEADER_SEARCH_PATHS], + ["$(inherited)", "/Bar/Sources/BarLib/include"] + ) XCTAssertEqual(settings[.MACH_O_TYPE], "mh_object") XCTAssertEqual(settings[.MODULEMAP_FILE_CONTENTS], """ - module BarLib { - umbrella "/Bar/Sources/BarLib/include" - export * - } - """) - XCTAssertEqual(settings[.MODULEMAP_PATH], "$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)/BarLib.modulemap") + module BarLib { + umbrella "/Bar/Sources/BarLib/include" + export * + } + """) + XCTAssertEqual( + settings[.MODULEMAP_PATH], + "$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)/BarLib.modulemap" + ) XCTAssertEqual(settings[.PACKAGE_RESOURCE_TARGET_KIND], "regular") XCTAssertEqual(settings[.PRODUCT_BUNDLE_IDENTIFIER], "BarLib") XCTAssertEqual(settings[.PRODUCT_MODULE_NAME], "BarLib") @@ -1440,13 +1647,14 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(settings[.HEADER_SEARCH_PATHS], ["$(inherited)", "/Bar/Sources/BarLib/include"]) XCTAssertEqual(settings[.OTHER_CFLAGS], [ "$(inherited)", - "-fmodule-map-file=$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)/BarLib.modulemap" + "-fmodule-map-file=$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)/BarLib.modulemap", ]) XCTAssertEqual(settings[.OTHER_LDRFLAGS], []) XCTAssertEqual(settings[.OTHER_LDFLAGS], ["$(inherited)", "-Wl,-no_warn_duplicate_libraries"]) XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS], [ "$(inherited)", - "-Xcc", "-fmodule-map-file=$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)/BarLib.modulemap" + "-Xcc", + "-fmodule-map-file=$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)/BarLib.modulemap", ]) } } @@ -1458,12 +1666,13 @@ class PIFBuilderTests: XCTestCase { #if !os(macOS) try XCTSkipIf(true, "test is only supported on macOS") #endif - let fs = InMemoryFileSystem(emptyFiles: - "/App/Sources/App/main.swift", - "/App/Sources/Logging/lib.swift", - "/App/Sources/Utils/lib.swift", - "/Bar/Sources/Lib/lib.swift", - "/Bar/Sources/Logging/lib.swift" + let fs = InMemoryFileSystem( + emptyFiles: + "/App/Sources/App/main.swift", + "/App/Sources/Logging/lib.swift", + "/App/Sources/Utils/lib.swift", + "/Bar/Sources/Lib/lib.swift", + "/Bar/Sources/Logging/lib.swift" ) let observability = ObservabilitySystem.makeForTesting() @@ -1482,7 +1691,8 @@ class PIFBuilderTests: XCTestCase { .init(name: "Utils", dependencies: [ .product(name: "BarLib", package: "Bar", moduleAliases: ["Logging": "BarLogging"]), ]), - ]), + ] + ), Manifest.createLocalSourceControlManifest( displayName: "Bar", path: "/Bar", @@ -1492,13 +1702,14 @@ class PIFBuilderTests: XCTestCase { targets: [ .init(name: "Lib", dependencies: ["Logging"]), .init(name: "Logging", dependencies: []), - ]), + ] + ), ], observabilityScope: observability.topScope ) var pif: PIF.TopLevelObject! - try withCustomEnv(["PKG_CONFIG_PATH": inputsDir.pathString]) { + try withCustomEnv(["PKG_CONFIG_PATH": self.inputsDir.pathString]) { let builder = PIFBuilder( graph: graph, parameters: .mock(), @@ -1567,7 +1778,6 @@ class PIFBuilderTests: XCTestCase { XCTAssertNil(settings[.SWIFT_MODULE_ALIASES]) } } - } project.checkTarget("PACKAGE-TARGET:Logging") { target in XCTAssertEqual(target.name, "Logging") @@ -1685,8 +1895,9 @@ class PIFBuilderTests: XCTestCase { #if !os(macOS) try XCTSkipIf(true, "test is only supported on macOS") #endif - let fs = InMemoryFileSystem(emptyFiles: - "/Bar/Sources/BarLib/lib.c" + let fs = InMemoryFileSystem( + emptyFiles: + "/Bar/Sources/BarLib/lib.c" ) let observability = ObservabilitySystem.makeForTesting() @@ -1704,13 +1915,14 @@ class PIFBuilderTests: XCTestCase { ], targets: [ .init(name: "BarLib"), - ]), + ] + ), ], observabilityScope: observability.topScope ) var pif: PIF.TopLevelObject! - try withCustomEnv(["PKG_CONFIG_PATH": inputsDir.pathString]) { + try withCustomEnv(["PKG_CONFIG_PATH": self.inputsDir.pathString]) { let builder = PIFBuilder( graph: graph, parameters: .mock(shouldCreateDylibForDynamicProducts: true), @@ -1737,9 +1949,10 @@ class PIFBuilderTests: XCTestCase { #if !os(macOS) try XCTSkipIf(true, "test is only supported on macOS") #endif - let fs = InMemoryFileSystem(emptyFiles: - "/Bar/Sources/BarLib/lib.c", - "/Bar/Sources/BarLib/module.modulemap" + let fs = InMemoryFileSystem( + emptyFiles: + "/Bar/Sources/BarLib/lib.c", + "/Bar/Sources/BarLib/module.modulemap" ) let observability = ObservabilitySystem.makeForTesting() @@ -1758,13 +1971,14 @@ class PIFBuilderTests: XCTestCase { ], targets: [ .init(name: "BarLib"), - ]), + ] + ), ], observabilityScope: observability.topScope ) var pif: PIF.TopLevelObject! - try withCustomEnv(["PKG_CONFIG_PATH": inputsDir.pathString]) { + try withCustomEnv(["PKG_CONFIG_PATH": self.inputsDir.pathString]) { let builder = PIFBuilder( graph: graph, parameters: .mock(shouldCreateDylibForDynamicProducts: true), @@ -1795,9 +2009,10 @@ class PIFBuilderTests: XCTestCase { #if !os(macOS) try XCTSkipIf(true, "test is only supported on macOS") #endif - let fs = InMemoryFileSystem(emptyFiles: - "/Foo/Sources/SystemLib1/module.modulemap", - "/Foo/Sources/SystemLib2/module.modulemap" + let fs = InMemoryFileSystem( + emptyFiles: + "/Foo/Sources/SystemLib1/module.modulemap", + "/Foo/Sources/SystemLib2/module.modulemap" ) let observability = ObservabilitySystem.makeForTesting() @@ -1813,13 +2028,14 @@ class PIFBuilderTests: XCTestCase { targets: [ .init(name: "SystemLib1", type: .system), .init(name: "SystemLib2", type: .system, pkgConfig: "Foo"), - ]), + ] + ), ], observabilityScope: observability.topScope ) var pif: PIF.TopLevelObject! - try withCustomEnv(["PKG_CONFIG_PATH": inputsDir.pathString]) { + try withCustomEnv(["PKG_CONFIG_PATH": self.inputsDir.pathString]) { let builder = PIFBuilder( graph: graph, parameters: .mock(), @@ -1854,12 +2070,12 @@ class PIFBuilderTests: XCTestCase { target.checkAllImpartedBuildSettings { settings in XCTAssertEqual(settings[.OTHER_CFLAGS], [ "$(inherited)", - "-fmodule-map-file=/Foo/Sources/SystemLib1/module.modulemap" + "-fmodule-map-file=/Foo/Sources/SystemLib1/module.modulemap", ]) XCTAssertEqual(settings[.OTHER_LDRFLAGS], []) XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS], [ "$(inherited)", - "-Xcc", "-fmodule-map-file=/Foo/Sources/SystemLib1/module.modulemap" + "-Xcc", "-fmodule-map-file=/Foo/Sources/SystemLib1/module.modulemap", ]) } } @@ -1887,20 +2103,20 @@ class PIFBuilderTests: XCTestCase { "$(inherited)", "-fmodule-map-file=/Foo/Sources/SystemLib2/module.modulemap", "-I/path/to/inc", - "-I\(self.inputsDir)" + "-I\(self.inputsDir)", ]) XCTAssertEqual(settings[.OTHER_LDFLAGS], [ "$(inherited)", "-L/usr/da/lib", "-lSystemModule", - "-lok" + "-lok", ]) XCTAssertEqual(settings[.OTHER_LDRFLAGS], []) XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS], [ "$(inherited)", "-Xcc", "-fmodule-map-file=/Foo/Sources/SystemLib2/module.modulemap", "-I/path/to/inc", - "-I\(self.inputsDir)" + "-I\(self.inputsDir)", ]) } } @@ -1912,7 +2128,8 @@ class PIFBuilderTests: XCTestCase { #if !os(macOS) try XCTSkipIf(true, "test is only supported on macOS") #endif - let fs = InMemoryFileSystem(emptyFiles: + let fs = InMemoryFileSystem( + emptyFiles: "/Foo/Sources/foo/main.swift", "/Foo/Sources/FooLib/lib.swift", "/Foo/Sources/FooTests/FooTests.swift", @@ -1934,13 +2151,14 @@ class PIFBuilderTests: XCTestCase { .init(name: "foo", dependencies: ["BinaryLibrary"]), .init(name: "FooLib", dependencies: ["BinaryLibrary"]), .init(name: "FooTests", dependencies: ["BinaryLibrary"], type: .test), - .init(name: "BinaryLibrary", path: "BinaryLibrary.xcframework", type: .binary) - ]), + .init(name: "BinaryLibrary", path: "BinaryLibrary.xcframework", type: .binary), + ] + ), ], binaryArtifacts: [ .plain("foo"): [ - "BinaryLibrary": .init(kind: .xcframework, originURL: nil, path: "/Foo/BinaryLibrary.xcframework") - ] + "BinaryLibrary": .init(kind: .xcframework, originURL: nil, path: "/Foo/BinaryLibrary.xcframework"), + ], ], shouldCreateMultipleTestProducts: true, observabilityScope: observability.topScope @@ -1981,16 +2199,17 @@ class PIFBuilderTests: XCTestCase { #if !os(macOS) try XCTSkipIf(true, "test is only supported on macOS") #endif - let fs = InMemoryFileSystem(emptyFiles: - "/Foo/Sources/foo/main.swift", - "/Foo/Sources/foo/Resources/Data.plist", - "/Foo/Sources/foo/Resources/Database.xcdatamodel", - "/Foo/Sources/FooLib/lib.swift", - "/Foo/Sources/FooLib/Resources/Data.plist", - "/Foo/Sources/FooLib/Resources/Database.xcdatamodel", - "/Foo/Sources/FooTests/FooTests.swift", - "/Foo/Sources/FooTests/Resources/Data.plist", - "/Foo/Sources/FooTests/Resources/Database.xcdatamodel" + let fs = InMemoryFileSystem( + emptyFiles: + "/Foo/Sources/foo/main.swift", + "/Foo/Sources/foo/Resources/Data.plist", + "/Foo/Sources/foo/Resources/Database.xcdatamodel", + "/Foo/Sources/FooLib/lib.swift", + "/Foo/Sources/FooLib/Resources/Data.plist", + "/Foo/Sources/FooLib/Resources/Database.xcdatamodel", + "/Foo/Sources/FooTests/FooTests.swift", + "/Foo/Sources/FooTests/Resources/Data.plist", + "/Foo/Sources/FooTests/Resources/Database.xcdatamodel" ) let observability = ObservabilitySystem.makeForTesting() @@ -2007,15 +2226,16 @@ class PIFBuilderTests: XCTestCase { targets: [ .init(name: "foo", resources: [ // This is intentionally specific to test that we pick up `.xcdatamodel` implicitly. - .init(rule: .process(localization: .none), path: "Resources/Data.plist") + .init(rule: .process(localization: .none), path: "Resources/Data.plist"), ]), .init(name: "FooLib", resources: [ - .init(rule: .process(localization: .none), path: "Resources") + .init(rule: .process(localization: .none), path: "Resources"), ]), .init(name: "FooTests", resources: [ - .init(rule: .process(localization: .none), path: "Resources") + .init(rule: .process(localization: .none), path: "Resources"), ], type: .test), - ]), + ] + ), ], shouldCreateMultipleTestProducts: true, useXCBuildFileRules: true, @@ -2203,10 +2423,11 @@ class PIFBuilderTests: XCTestCase { #if !os(macOS) try XCTSkipIf(true, "test is only supported on macOS") #endif - let fs = InMemoryFileSystem(emptyFiles: - "/Foo/Sources/foo/main.swift", - "/Foo/Sources/FooLib/lib.swift", - "/Foo/Sources/FooTests/FooTests.swift" + let fs = InMemoryFileSystem( + emptyFiles: + "/Foo/Sources/foo/main.swift", + "/Foo/Sources/FooLib/lib.swift", + "/Foo/Sources/FooTests/FooTests.swift" ) let toolsVersion: ToolsVersion = if isPackageAccessModifierSupported { .v5_9 } else { .v5 } @@ -2229,54 +2450,66 @@ class PIFBuilderTests: XCTestCase { settings: [ .init( tool: .c, - kind: .define("ENABLE_BEST_MODE")), + kind: .define("ENABLE_BEST_MODE") + ), .init( tool: .cxx, kind: .headerSearchPath("some/path"), - condition: .init(platformNames: ["macos"])), + condition: .init(platformNames: ["macos"]) + ), .init( tool: .linker, kind: .linkedLibrary("z"), - condition: .init(config: "debug")), + condition: .init(config: "debug") + ), .init( tool: .swift, kind: .unsafeFlags(["-secret", "value"]), - condition: .init(platformNames: ["macos", "linux"], config: "release")), + condition: .init(platformNames: ["macos", "linux"], config: "release") + ), ] ), .init(name: "FooLib", settings: [ .init( tool: .c, - kind: .define("ENABLE_BEST_MODE")), + kind: .define("ENABLE_BEST_MODE") + ), .init( tool: .cxx, kind: .headerSearchPath("some/path"), - condition: .init(platformNames: ["macos"])), + condition: .init(platformNames: ["macos"]) + ), .init( tool: .linker, kind: .linkedLibrary("z"), - condition: .init(config: "debug")), + condition: .init(config: "debug") + ), .init( tool: .swift, kind: .unsafeFlags(["-secret", "value"]), - condition: .init(platformNames: ["macos", "linux"], config: "release")), + condition: .init(platformNames: ["macos", "linux"], config: "release") + ), ]), .init(name: "FooTests", type: .test, settings: [ .init( tool: .c, - kind: .define("ENABLE_BEST_MODE")), + kind: .define("ENABLE_BEST_MODE") + ), .init( tool: .cxx, kind: .headerSearchPath("some/path"), - condition: .init(platformNames: ["macos"])), + condition: .init(platformNames: ["macos"]) + ), .init( tool: .linker, kind: .linkedLibrary("z"), - condition: .init(config: "debug")), + condition: .init(config: "debug") + ), .init( tool: .swift, kind: .unsafeFlags(["-secret", "value"]), - condition: .init(platformNames: ["macos", "linux"], config: "release")), + condition: .init(platformNames: ["macos", "linux"], config: "release") + ), ]), ] ), @@ -2306,11 +2539,14 @@ class PIFBuilderTests: XCTestCase { project.checkTarget("PACKAGE-PRODUCT:foo") { target in target.checkBuildConfiguration("Debug") { configuration in configuration.checkBuildSettings { settings in - XCTAssertEqual(settings[.GCC_PREPROCESSOR_DEFINITIONS], ["$(inherited)", "ENABLE_BEST_MODE"]) + XCTAssertEqual( + settings[.GCC_PREPROCESSOR_DEFINITIONS], + ["$(inherited)", "ENABLE_BEST_MODE"] + ) XCTAssertEqual(settings[.HEADER_SEARCH_PATHS], nil) XCTAssertEqual(settings[.HEADER_SEARCH_PATHS, for: .macOS], [ "$(inherited)", - "/Foo/Sources/foo/some/path" + "/Foo/Sources/foo/some/path", ]) XCTAssertEqual(settings[.OTHER_LDFLAGS], ["$(inherited)", "-lz"]) XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS], packageNameOptions) @@ -2319,16 +2555,25 @@ class PIFBuilderTests: XCTestCase { target.checkBuildConfiguration("Release") { configuration in configuration.checkBuildSettings { settings in - XCTAssertEqual(settings[.GCC_PREPROCESSOR_DEFINITIONS], ["$(inherited)", "ENABLE_BEST_MODE"]) + XCTAssertEqual( + settings[.GCC_PREPROCESSOR_DEFINITIONS], + ["$(inherited)", "ENABLE_BEST_MODE"] + ) XCTAssertEqual(settings[.HEADER_SEARCH_PATHS], nil) XCTAssertEqual(settings[.HEADER_SEARCH_PATHS, for: .macOS], [ "$(inherited)", - "/Foo/Sources/foo/some/path" + "/Foo/Sources/foo/some/path", ]) XCTAssertEqual(settings[.OTHER_LDFLAGS], nil) XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS], packageNameOptions) - XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS, for: .macOS], ["$(inherited)", "-secret", "value"]) - XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS, for: .linux], ["$(inherited)", "-secret", "value"]) + XCTAssertEqual( + settings[.OTHER_SWIFT_FLAGS, for: .macOS], + ["$(inherited)", "-secret", "value"] + ) + XCTAssertEqual( + settings[.OTHER_SWIFT_FLAGS, for: .linux], + ["$(inherited)", "-secret", "value"] + ) } } } @@ -2358,11 +2603,14 @@ class PIFBuilderTests: XCTestCase { project.checkTarget("PACKAGE-TARGET:FooLib") { target in target.checkBuildConfiguration("Debug") { configuration in configuration.checkBuildSettings { settings in - XCTAssertEqual(settings[.GCC_PREPROCESSOR_DEFINITIONS], ["$(inherited)", "ENABLE_BEST_MODE"]) + XCTAssertEqual( + settings[.GCC_PREPROCESSOR_DEFINITIONS], + ["$(inherited)", "ENABLE_BEST_MODE"] + ) XCTAssertEqual(settings[.HEADER_SEARCH_PATHS], nil) XCTAssertEqual(settings[.HEADER_SEARCH_PATHS, for: .macOS], [ "$(inherited)", - "/Foo/Sources/FooLib/some/path" + "/Foo/Sources/FooLib/some/path", ]) XCTAssertEqual(settings[.OTHER_LDFLAGS], ["$(inherited)", "-lz"]) XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS], packageNameOptions) @@ -2371,23 +2619,35 @@ class PIFBuilderTests: XCTestCase { target.checkBuildConfiguration("Release") { configuration in configuration.checkBuildSettings { settings in - XCTAssertEqual(settings[.GCC_PREPROCESSOR_DEFINITIONS], ["$(inherited)", "ENABLE_BEST_MODE"]) + XCTAssertEqual( + settings[.GCC_PREPROCESSOR_DEFINITIONS], + ["$(inherited)", "ENABLE_BEST_MODE"] + ) XCTAssertEqual(settings[.HEADER_SEARCH_PATHS], nil) XCTAssertEqual(settings[.HEADER_SEARCH_PATHS, for: .macOS], [ "$(inherited)", - "/Foo/Sources/FooLib/some/path" + "/Foo/Sources/FooLib/some/path", ]) XCTAssertEqual(settings[.OTHER_LDFLAGS], nil) XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS], packageNameOptions) - XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS, for: .macOS], ["$(inherited)", "-secret", "value"]) - XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS, for: .linux], ["$(inherited)", "-secret", "value"]) + XCTAssertEqual( + settings[.OTHER_SWIFT_FLAGS, for: .macOS], + ["$(inherited)", "-secret", "value"] + ) + XCTAssertEqual( + settings[.OTHER_SWIFT_FLAGS, for: .linux], + ["$(inherited)", "-secret", "value"] + ) } } target.checkImpartedBuildSettings { settings in XCTAssertEqual(settings[.GCC_PREPROCESSOR_DEFINITIONS], nil) XCTAssertEqual(settings[.HEADER_SEARCH_PATHS], nil) - XCTAssertEqual(settings[.OTHER_LDFLAGS], ["$(inherited)", "-Wl,-no_warn_duplicate_libraries", "-lz"]) + XCTAssertEqual( + settings[.OTHER_LDFLAGS], + ["$(inherited)", "-Wl,-no_warn_duplicate_libraries", "-lz"] + ) XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS], nil) } } @@ -2395,11 +2655,14 @@ class PIFBuilderTests: XCTestCase { project.checkTarget("PACKAGE-PRODUCT:FooTests") { target in target.checkBuildConfiguration("Debug") { configuration in configuration.checkBuildSettings { settings in - XCTAssertEqual(settings[.GCC_PREPROCESSOR_DEFINITIONS], ["$(inherited)", "ENABLE_BEST_MODE"]) + XCTAssertEqual( + settings[.GCC_PREPROCESSOR_DEFINITIONS], + ["$(inherited)", "ENABLE_BEST_MODE"] + ) XCTAssertEqual(settings[.HEADER_SEARCH_PATHS], nil) XCTAssertEqual(settings[.HEADER_SEARCH_PATHS, for: .macOS], [ "$(inherited)", - "/Foo/Sources/FooTests/some/path" + "/Foo/Sources/FooTests/some/path", ]) XCTAssertEqual(settings[.OTHER_LDFLAGS], ["$(inherited)", "-lz"]) XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS], packageNameOptions) @@ -2408,16 +2671,25 @@ class PIFBuilderTests: XCTestCase { target.checkBuildConfiguration("Release") { configuration in configuration.checkBuildSettings { settings in - XCTAssertEqual(settings[.GCC_PREPROCESSOR_DEFINITIONS], ["$(inherited)", "ENABLE_BEST_MODE"]) + XCTAssertEqual( + settings[.GCC_PREPROCESSOR_DEFINITIONS], + ["$(inherited)", "ENABLE_BEST_MODE"] + ) XCTAssertEqual(settings[.HEADER_SEARCH_PATHS], nil) XCTAssertEqual(settings[.HEADER_SEARCH_PATHS, for: .macOS], [ "$(inherited)", - "/Foo/Sources/FooTests/some/path" + "/Foo/Sources/FooTests/some/path", ]) XCTAssertEqual(settings[.OTHER_LDFLAGS], nil) XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS], packageNameOptions) - XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS, for: .macOS], ["$(inherited)", "-secret", "value"]) - XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS, for: .linux], ["$(inherited)", "-secret", "value"]) + XCTAssertEqual( + settings[.OTHER_SWIFT_FLAGS, for: .macOS], + ["$(inherited)", "-secret", "value"] + ) + XCTAssertEqual( + settings[.OTHER_SWIFT_FLAGS, for: .linux], + ["$(inherited)", "-secret", "value"] + ) } } } @@ -2426,22 +2698,23 @@ class PIFBuilderTests: XCTestCase { } func testBuildSettings() throws { - try buildSettingsTestCase(isPackageAccessModifierSupported: false) + try self.buildSettingsTestCase(isPackageAccessModifierSupported: false) } func testBuildSettingsPackageAccess() throws { - try buildSettingsTestCase(isPackageAccessModifierSupported: true) + try self.buildSettingsTestCase(isPackageAccessModifierSupported: true) } func testConditionalDependencies() throws { #if !os(macOS) try XCTSkipIf(true, "test is only supported on macOS") #endif - let fs = InMemoryFileSystem(emptyFiles: - "/Foo/Sources/foo/main.swift", - "/Foo/Sources/FooLib1/lib.swift", - "/Foo/Sources/FooLib2/lib.swift", - "/Foo/Sources/FooTests/FooTests.swift" + let fs = InMemoryFileSystem( + emptyFiles: + "/Foo/Sources/foo/main.swift", + "/Foo/Sources/FooLib1/lib.swift", + "/Foo/Sources/FooLib2/lib.swift", + "/Foo/Sources/FooTests/FooTests.swift" ) let observability = ObservabilitySystem.makeForTesting() @@ -2460,7 +2733,8 @@ class PIFBuilderTests: XCTestCase { ]), .init(name: "FooLib1"), .init(name: "FooLib2"), - ]), + ] + ), ], shouldCreateMultipleTestProducts: true, observabilityScope: observability.topScope @@ -2487,18 +2761,28 @@ class PIFBuilderTests: XCTestCase { XCTAssertEqual(target.dependencies, ["PACKAGE-TARGET:FooLib1", "PACKAGE-TARGET:FooLib2"]) XCTAssertEqual(target.frameworks, ["PACKAGE-TARGET:FooLib1", "PACKAGE-TARGET:FooLib2"]) - let dependencyMap = Dictionary(uniqueKeysWithValues: target.baseTarget.dependencies.map{ ($0.targetGUID, $0.platformFilters) }) + let dependencyMap = Dictionary(uniqueKeysWithValues: target.baseTarget.dependencies.map { ( + $0.targetGUID, + $0.platformFilters + ) }) XCTAssertEqual(dependencyMap, expectedFilters) - let frameworksBuildFiles = target.baseTarget.buildPhases.first{ $0 is PIF.FrameworksBuildPhase }?.buildFiles ?? [] - let frameworksBuildFilesMap = Dictionary(uniqueKeysWithValues: frameworksBuildFiles.compactMap{ file -> (PIF.GUID, [PIF.PlatformFilter])? in - switch file.reference { - case .target(let guid): - return (guid, file.platformFilters) - case .file: - return nil - } - }) + let frameworksBuildFiles = target.baseTarget.buildPhases.first { $0 is PIF.FrameworksBuildPhase }? + .buildFiles ?? [] + let frameworksBuildFilesMap = Dictionary( + uniqueKeysWithValues: frameworksBuildFiles + .compactMap { file -> ( + PIF.GUID, + [PIF.PlatformFilter] + )? in + switch file.reference { + case .target(let guid): + return (guid, file.platformFilters) + case .file: + return nil + } + } + ) XCTAssertEqual(dependencyMap, frameworksBuildFilesMap) } } @@ -2509,8 +2793,9 @@ class PIFBuilderTests: XCTestCase { #if !os(macOS) try XCTSkipIf(true, "test is only supported on macOS") #endif - let fs = InMemoryFileSystem(emptyFiles: - "/Foo/Sources/foo/main.swift" + let fs = InMemoryFileSystem( + emptyFiles: + "/Foo/Sources/foo/main.swift" ) let observability = ObservabilitySystem.makeForTesting() @@ -2526,7 +2811,8 @@ class PIFBuilderTests: XCTestCase { toolsVersion: .v5_3, targets: [ .init(name: "foo", dependencies: []), - ]), + ] + ), ], shouldCreateMultipleTestProducts: true, observabilityScope: observability.topScope @@ -2559,8 +2845,9 @@ class PIFBuilderTests: XCTestCase { #if !os(macOS) try XCTSkipIf(true, "test is only supported on macOS") #endif - let fs = InMemoryFileSystem(emptyFiles: - "/MyLib/Sources/MyLib/Foo.swift" + let fs = InMemoryFileSystem( + emptyFiles: + "/MyLib/Sources/MyLib/Foo.swift" ) let observability = ObservabilitySystem.makeForTesting() @@ -2579,9 +2866,11 @@ class PIFBuilderTests: XCTestCase { .init( tool: .swift, kind: .unsafeFlags(["-enable-library-evolution"]), - condition: .init(config: "release")), + condition: .init(config: "release") + ), ]), - ]), + ] + ), ], shouldCreateMultipleTestProducts: true, observabilityScope: observability.topScope @@ -2609,7 +2898,8 @@ class PIFBuilderTests: XCTestCase { } target.checkBuildConfiguration("Release") { configuration in configuration.checkBuildSettings { settings in - // Check that the `-enable-library-evolution` setting for Release also set SWIFT_EMIT_MODULE_INTERFACE. + // Check that the `-enable-library-evolution` setting for Release also set + // SWIFT_EMIT_MODULE_INTERFACE. XCTAssertEqual(settings[.SWIFT_EMIT_MODULE_INTERFACE], "YES") XCTAssertEqual(settings[.OTHER_SWIFT_FLAGS], ["$(inherited)", "-enable-library-evolution"]) } @@ -2618,12 +2908,63 @@ class PIFBuilderTests: XCTestCase { } } } + + func testSupportedSwiftVersions() throws { + #if !os(macOS) + try XCTSkipIf(true, "test is only supported on macOS") + #endif + let fs = InMemoryFileSystem( + emptyFiles: + "/Foo/Sources/foo/main.swift" + ) + + let observability = ObservabilitySystem.makeForTesting() + let graph = try loadModulesGraph( + fileSystem: fs, + manifests: [ + Manifest.createRootManifest( + displayName: "Foo", + path: "/Foo", + toolsVersion: .v5_3, + swiftLanguageVersions: [.v4_2, .v5], + targets: [ + .init(name: "foo", dependencies: []), + ] + ), + ], + shouldCreateMultipleTestProducts: true, + observabilityScope: observability.topScope + ) + + let builder = PIFBuilder( + graph: graph, + parameters: .mock(supportedSwiftVersions: [.v4_2]), + fileSystem: fs, + observabilityScope: observability.topScope + ) + let pif = try builder.construct() + + XCTAssertNoDiagnostics(observability.diagnostics) + + try PIFTester(pif) { workspace in + try workspace.checkProject("PACKAGE:/Foo") { project in + project.checkTarget("PACKAGE-PRODUCT:foo") { target in + target.checkBuildConfiguration("Debug") { configuration in + configuration.checkBuildSettings { settings in + XCTAssertEqual(settings[.SWIFT_VERSION], "4.2") + } + } + } + } + } + } } extension PIFBuilderParameters { static func mock( isPackageAccessModifierSupported: Bool = false, - shouldCreateDylibForDynamicProducts: Bool = false + shouldCreateDylibForDynamicProducts: Bool = false, + supportedSwiftVersions: [SwiftLanguageVersion] = [] ) -> Self { PIFBuilderParameters( isPackageAccessModifierSupported: isPackageAccessModifierSupported, @@ -2631,7 +2972,8 @@ extension PIFBuilderParameters { shouldCreateDylibForDynamicProducts: shouldCreateDylibForDynamicProducts, toolchainLibDir: "/toolchain/lib", pkgConfigDirectories: ["/pkg-config"], - sdkRootPath: "/some.sdk" + sdkRootPath: "/some.sdk", + supportedSwiftVersions: supportedSwiftVersions ) } } From 6f12e54169dec46da302b9081a2d072d339cdcf1 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 15 Apr 2024 12:54:30 -0700 Subject: [PATCH 105/159] [PackageGraph] Remove uses of `availableLibraries` from ModulesGraph.load and related methods --- .../PackageGraph/ModulesGraph+Loading.swift | 45 ++++++++----------- Sources/Workspace/Workspace.swift | 1 - Sources/swift-bootstrap/main.swift | 1 - .../PackageGraphPerfTests.swift | 1 - 4 files changed, 18 insertions(+), 30 deletions(-) diff --git a/Sources/PackageGraph/ModulesGraph+Loading.swift b/Sources/PackageGraph/ModulesGraph+Loading.swift index 59687da73f3..b72e3fb265e 100644 --- a/Sources/PackageGraph/ModulesGraph+Loading.swift +++ b/Sources/PackageGraph/ModulesGraph+Loading.swift @@ -32,7 +32,6 @@ extension ModulesGraph { customPlatformsRegistry: PlatformRegistry? = .none, customXCTestMinimumDeploymentTargets: [PackageModel.Platform: PlatformVersion]? = .none, testEntryPointPath: AbsolutePath? = nil, - availableLibraries: [LibraryMetadata], fileSystem: FileSystem, observabilityScope: ObservabilityScope ) throws -> ModulesGraph { @@ -160,7 +159,6 @@ extension ModulesGraph { unsafeAllowedPackages: unsafeAllowedPackages, platformRegistry: customPlatformsRegistry ?? .default, platformVersionProvider: platformVersionProvider, - availableLibraries: availableLibraries, fileSystem: fileSystem, observabilityScope: observabilityScope ) @@ -250,7 +248,6 @@ private func createResolvedPackages( unsafeAllowedPackages: Set, platformRegistry: PlatformRegistry, platformVersionProvider: PlatformVersionProvider, - availableLibraries: [LibraryMetadata], fileSystem: FileSystem, observabilityScope: ObservabilityScope ) throws -> [ResolvedPackage] { @@ -529,32 +526,26 @@ private func createResolvedPackages( t.name != productRef.name } - let identitiesAvailableInSDK = availableLibraries.flatMap { $0.identities.map { $0.identity } } - // TODO: Do we have to care about "name" vs. identity here? - if let name = productRef.package, identitiesAvailableInSDK.contains(PackageIdentity.plain(name)) { - // Do not emit any diagnostic. - } else { - // Find a product name from the available product dependencies that is most similar to the required product name. - let bestMatchedProductName = bestMatch(for: productRef.name, from: Array(allTargetNames)) - var packageContainingBestMatchedProduct: String? - if let bestMatchedProductName, productRef.name == bestMatchedProductName { - let dependentPackages = packageBuilder.dependencies.map(\.package) - for p in dependentPackages where p.targets.contains(where: { $0.name == bestMatchedProductName }) { - packageContainingBestMatchedProduct = p.identity.description - break - } + // Find a product name from the available product dependencies that is most similar to the required product name. + let bestMatchedProductName = bestMatch(for: productRef.name, from: Array(allTargetNames)) + var packageContainingBestMatchedProduct: String? + if let bestMatchedProductName, productRef.name == bestMatchedProductName { + let dependentPackages = packageBuilder.dependencies.map(\.package) + for p in dependentPackages where p.targets.contains(where: { $0.name == bestMatchedProductName }) { + packageContainingBestMatchedProduct = p.identity.description + break } - let error = PackageGraphError.productDependencyNotFound( - package: package.identity.description, - targetName: targetBuilder.target.name, - dependencyProductName: productRef.name, - dependencyPackageName: productRef.package, - dependencyProductInDecl: !declProductsAsDependency.isEmpty, - similarProductName: bestMatchedProductName, - packageContainingSimilarProduct: packageContainingBestMatchedProduct - ) - packageObservabilityScope.emit(error) } + let error = PackageGraphError.productDependencyNotFound( + package: package.identity.description, + targetName: targetBuilder.target.name, + dependencyProductName: productRef.name, + dependencyPackageName: productRef.package, + dependencyProductInDecl: !declProductsAsDependency.isEmpty, + similarProductName: bestMatchedProductName, + packageContainingSimilarProduct: packageContainingBestMatchedProduct + ) + packageObservabilityScope.emit(error) } continue } diff --git a/Sources/Workspace/Workspace.swift b/Sources/Workspace/Workspace.swift index e6016e3d554..802bc1065c9 100644 --- a/Sources/Workspace/Workspace.swift +++ b/Sources/Workspace/Workspace.swift @@ -924,7 +924,6 @@ extension Workspace { createREPLProduct: self.configuration.createREPLProduct, customXCTestMinimumDeploymentTargets: customXCTestMinimumDeploymentTargets, testEntryPointPath: testEntryPointPath, - availableLibraries: self.providedLibraries, fileSystem: self.fileSystem, observabilityScope: observabilityScope ) diff --git a/Sources/swift-bootstrap/main.swift b/Sources/swift-bootstrap/main.swift index 6cf38354370..5b7ca511708 100644 --- a/Sources/swift-bootstrap/main.swift +++ b/Sources/swift-bootstrap/main.swift @@ -390,7 +390,6 @@ struct SwiftBootstrapBuildTool: ParsableCommand { partial[item.key] = (manifest: item.value, fs: self.fileSystem) }, binaryArtifacts: [:], - availableLibraries: [], // assume no provided libraries during bootstrap fileSystem: fileSystem, observabilityScope: observabilityScope ) diff --git a/Tests/PackageGraphPerformanceTests/PackageGraphPerfTests.swift b/Tests/PackageGraphPerformanceTests/PackageGraphPerfTests.swift index 78b0298697e..c469caa9e88 100644 --- a/Tests/PackageGraphPerformanceTests/PackageGraphPerfTests.swift +++ b/Tests/PackageGraphPerformanceTests/PackageGraphPerfTests.swift @@ -97,7 +97,6 @@ final class PackageGraphPerfTests: XCTestCasePerf { identityResolver: identityResolver, externalManifests: externalManifests, binaryArtifacts: [:], - availableLibraries: [], // assume no provided libraries for testing. fileSystem: fs, observabilityScope: observability.topScope ) From 6d0f18ee441dffe1ac151fbac5a3cb8aee97aafa Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 16 Apr 2024 09:11:07 -0700 Subject: [PATCH 106/159] Add new `providedLibrary` package reference kind This kind of reference is going to be returned by package resolution if it detects that a provided library version matches all of the constraints. This makes it easier to handle provided libraries post-resolution instead of eliding them completely. --- Sources/Build/BuildPlan/BuildPlan.swift | 2 +- Sources/PackageGraph/ModulesGraph.swift | 3 +- .../PubGrub/PubGrubDependencyResolver.swift | 2 +- .../PackageLoading/ManifestJSONParser.swift | 2 +- Sources/PackageModel/IdentityResolver.swift | 2 + Sources/PackageModel/PackageReference.swift | 18 ++++- .../Plugins/PluginContextSerializer.swift | 2 + .../SPMTestSupport/MockManifestLoader.swift | 6 ++ Sources/SPMTestSupport/MockWorkspace.swift | 2 + Sources/Workspace/CMakeLists.txt | 1 + Sources/Workspace/Diagnostics.swift | 2 +- .../ProvidedLibraryPackageContainer.swift | 74 +++++++++++++++++++ .../Workspace+PackageContainer.swift | 8 ++ Sources/Workspace/Workspace+State.swift | 12 +++ Sources/Workspace/Workspace.swift | 2 + .../PackageLoadingTests/PDLoadingTests.swift | 2 + 16 files changed, 134 insertions(+), 6 deletions(-) create mode 100644 Sources/Workspace/PackageContainer/ProvidedLibraryPackageContainer.swift diff --git a/Sources/Build/BuildPlan/BuildPlan.swift b/Sources/Build/BuildPlan/BuildPlan.swift index 8f03a297328..123d34e3e55 100644 --- a/Sources/Build/BuildPlan/BuildPlan.swift +++ b/Sources/Build/BuildPlan/BuildPlan.swift @@ -740,7 +740,7 @@ extension ResolvedPackage { switch self.underlying.manifest.packageKind { case .registry, .remoteSourceControl, .localSourceControl: return true - case .root, .fileSystem: + case .root, .fileSystem, .providedLibrary: return false } } diff --git a/Sources/PackageGraph/ModulesGraph.swift b/Sources/PackageGraph/ModulesGraph.swift index 8160fc5f84a..ba29c9ef906 100644 --- a/Sources/PackageGraph/ModulesGraph.swift +++ b/Sources/PackageGraph/ModulesGraph.swift @@ -358,7 +358,8 @@ extension PackageGraphError: CustomStringConvertible { switch $0.manifest.packageKind { case .root(let path), .fileSystem(let path), - .localSourceControl(let path): + .localSourceControl(let path), + .providedLibrary(let path): description += " (at '\(path)')" case .remoteSourceControl(let url): description += " (from '\(url)')" diff --git a/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift b/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift index ffee1492d1e..553b354790a 100644 --- a/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift +++ b/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift @@ -897,7 +897,7 @@ extension PackageRequirement { extension PackageReference { public func matchingPrebuiltLibrary(in availableLibraries: [LibraryMetadata]) -> LibraryMetadata? { switch self.kind { - case .fileSystem, .localSourceControl, .root: + case .fileSystem, .localSourceControl, .root, .providedLibrary: return nil // can never match a prebuilt library case .registry(let identity): if let registryIdentity = identity.registry { diff --git a/Sources/PackageLoading/ManifestJSONParser.swift b/Sources/PackageLoading/ManifestJSONParser.swift index 564e5991982..7b95ae7ab93 100644 --- a/Sources/PackageLoading/ManifestJSONParser.swift +++ b/Sources/PackageLoading/ManifestJSONParser.swift @@ -83,7 +83,7 @@ enum ManifestJSONParser { case .localSourceControl(let _packagePath): // we have a more accurate path than the virtual one packagePath = _packagePath - case .root(let _packagePath), .fileSystem(let _packagePath): + case .root(let _packagePath), .fileSystem(let _packagePath), .providedLibrary(let _packagePath): // we dont have a more accurate path, and they should be the same // asserting (debug only) to make sure refactoring is correct 11/2023 assert(packagePath == _packagePath, "expecting package path '\(packagePath)' to be the same as '\(_packagePath)'") diff --git a/Sources/PackageModel/IdentityResolver.swift b/Sources/PackageModel/IdentityResolver.swift index 847836ecf2d..63bc0b560c9 100644 --- a/Sources/PackageModel/IdentityResolver.swift +++ b/Sources/PackageModel/IdentityResolver.swift @@ -46,6 +46,8 @@ public struct DefaultIdentityResolver: IdentityResolver { return try self.resolveIdentity(for: url) case .registry(let identity): return identity + case .providedLibrary(let path): + return try self.resolveIdentity(for: path) } } diff --git a/Sources/PackageModel/PackageReference.swift b/Sources/PackageModel/PackageReference.swift index ff6b448101c..448909fe03b 100644 --- a/Sources/PackageModel/PackageReference.swift +++ b/Sources/PackageModel/PackageReference.swift @@ -34,6 +34,9 @@ public struct PackageReference { /// A package from a registry. case registry(PackageIdentity) + /// A prebuilt library provided by a toolchain + case providedLibrary(AbsolutePath) + // FIXME: we should not need this once we migrate off URLs //@available(*, deprecated) public var locationString: String { @@ -49,6 +52,8 @@ public struct PackageReference { case .registry(let identity): // FIXME: this is a placeholder return identity.description + case .providedLibrary(let path): + return path.pathString } } @@ -70,6 +75,8 @@ public struct PackageReference { return "remoteSourceControl \(url)" case .registry(let identity): return "registry \(identity)" + case .providedLibrary(let path): + return "library \(path)" } } @@ -124,6 +131,8 @@ public struct PackageReference { case .registry(let identity): // FIXME: this is a placeholder self.deprecatedName = name ?? identity.description + case .providedLibrary(let path): + self.deprecatedName = name ?? PackageIdentityParser.computeDefaultName(fromPath: path) } } @@ -151,6 +160,10 @@ public struct PackageReference { public static func registry(identity: PackageIdentity) -> PackageReference { PackageReference(identity: identity, kind: .registry(identity)) } + + public static func library(identity: PackageIdentity, path: AbsolutePath) -> PackageReference { + PackageReference(identity: identity, kind: .providedLibrary(path)) + } } extension PackageReference: Equatable { @@ -203,7 +216,7 @@ extension PackageReference: CustomStringConvertible { extension PackageReference.Kind: Encodable { private enum CodingKeys: String, CodingKey { - case root, fileSystem, localSourceControl, remoteSourceControl, registry + case root, fileSystem, localSourceControl, remoteSourceControl, registry, providedLibrary } public func encode(to encoder: Encoder) throws { @@ -224,6 +237,9 @@ extension PackageReference.Kind: Encodable { case .registry: var unkeyedContainer = container.nestedUnkeyedContainer(forKey: .registry) try unkeyedContainer.encode(self.isRoot) + case .providedLibrary(let path): + var unkeyedContainer = container.nestedUnkeyedContainer(forKey: .providedLibrary) + try unkeyedContainer.encode(path) } } } diff --git a/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift b/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift index 9edfb0a7bb4..3816f7791b5 100644 --- a/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift +++ b/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift @@ -240,6 +240,8 @@ internal struct PluginContextSerializer { return .repository(url: url.absoluteString, displayVersion: String(describing: package.manifest.version), scmRevision: String(describing: package.manifest.revision)) case .registry(let identity): return .registry(identity: identity.description, displayVersion: String(describing: package.manifest.version)) + case .providedLibrary(_): + throw InternalError("provided libraries are not supported in plugin context") } } diff --git a/Sources/SPMTestSupport/MockManifestLoader.swift b/Sources/SPMTestSupport/MockManifestLoader.swift index fc1f345ed64..08b1bf32540 100644 --- a/Sources/SPMTestSupport/MockManifestLoader.swift +++ b/Sources/SPMTestSupport/MockManifestLoader.swift @@ -109,6 +109,9 @@ extension ManifestLoader { packageIdentity = identity // FIXME: placeholder packageLocation = identity.description + case .providedLibrary(let path): + packageIdentity = try identityResolver.resolveIdentity(for: path) + packageLocation = path.pathString } return try await self.load( manifestPath: manifestPath, @@ -156,6 +159,9 @@ extension ManifestLoader { packageIdentity = identity // FIXME: placeholder packageLocation = identity.description + case .providedLibrary(let path): + packageIdentity = try identityResolver.resolveIdentity(for: path) + packageLocation = path.pathString } return try await self.load( packagePath: packagePath, diff --git a/Sources/SPMTestSupport/MockWorkspace.swift b/Sources/SPMTestSupport/MockWorkspace.swift index 717fc4f50ee..81056281f22 100644 --- a/Sources/SPMTestSupport/MockWorkspace.swift +++ b/Sources/SPMTestSupport/MockWorkspace.swift @@ -1041,6 +1041,8 @@ extension PackageReference.Kind { return "remoteSourceControl" case .registry: return "registry" + case .providedLibrary: + return "library" } } } diff --git a/Sources/Workspace/CMakeLists.txt b/Sources/Workspace/CMakeLists.txt index 7817b6b4a43..447c3353684 100644 --- a/Sources/Workspace/CMakeLists.txt +++ b/Sources/Workspace/CMakeLists.txt @@ -15,6 +15,7 @@ add_library(Workspace ManagedArtifact.swift ManagedDependency.swift PackageContainer/FileSystemPackageContainer.swift + PackageContainer/ProvidedLibraryPackageContainer.swift PackageContainer/RegistryPackageContainer.swift PackageContainer/SourceControlPackageContainer.swift ResolvedFileWatcher.swift diff --git a/Sources/Workspace/Diagnostics.swift b/Sources/Workspace/Diagnostics.swift index 8bf918875bc..007214647d1 100644 --- a/Sources/Workspace/Diagnostics.swift +++ b/Sources/Workspace/Diagnostics.swift @@ -185,7 +185,7 @@ extension Basics.Diagnostic { return "'\(identity.description)'" case .remoteSourceControl(let url): return "'\($0.identity)' from \(url)" - case .localSourceControl(let path), .fileSystem(let path), .root(let path): + case .localSourceControl(let path), .fileSystem(let path), .root(let path), .providedLibrary(let path): return "'\($0.identity)' at \(path)" } } diff --git a/Sources/Workspace/PackageContainer/ProvidedLibraryPackageContainer.swift b/Sources/Workspace/PackageContainer/ProvidedLibraryPackageContainer.swift new file mode 100644 index 00000000000..f423dcf454a --- /dev/null +++ b/Sources/Workspace/PackageContainer/ProvidedLibraryPackageContainer.swift @@ -0,0 +1,74 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Basics +import Dispatch +import PackageGraph +import PackageLoading +import PackageModel + +import struct TSCUtility.Version + +public struct ProvidedLibraryPackageContainer: PackageContainer { + public let package: PackageReference + + /// Observability scope to emit diagnostics + private let observabilityScope: ObservabilityScope + + public init( + package: PackageReference, + observabilityScope: ObservabilityScope + ) throws { + switch package.kind { + case .providedLibrary: + break + default: + throw InternalError("invalid package type \(package.kind)") + } + self.package = package + self.observabilityScope = observabilityScope.makeChildScope( + description: "ProvidedLibraryPackageContainer", + metadata: package.diagnosticsMetadata) + } + + public func isToolsVersionCompatible(at version: Version) -> Bool { + true + } + + public func toolsVersion(for version: Version) throws -> ToolsVersion { + .v6_0 + } + + public func toolsVersionsAppropriateVersionsDescending() throws -> [Version] { + return try versionsDescending() + } + + public func versionsAscending() throws -> [Version] { + [] // TODO + } + + public func getDependencies(at version: Version, productFilter: ProductFilter) throws -> [PackageContainerConstraint] { + [] + } + + public func getDependencies(at revision: String, productFilter: ProductFilter) throws -> [PackageContainerConstraint] { + [] + } + + public func getUnversionedDependencies(productFilter: ProductFilter) throws -> [PackageContainerConstraint] { + [] + } + + public func loadPackageReference(at boundVersion: BoundVersion) throws -> PackageReference { + package + } +} diff --git a/Sources/Workspace/Workspace+PackageContainer.swift b/Sources/Workspace/Workspace+PackageContainer.swift index 4787e3adef4..4ba1016756a 100644 --- a/Sources/Workspace/Workspace+PackageContainer.swift +++ b/Sources/Workspace/Workspace+PackageContainer.swift @@ -93,6 +93,14 @@ extension Workspace: PackageContainerProvider { queue.async { completion(.success(container)) } + + case .providedLibrary: + let container = try ProvidedLibraryPackageContainer( + package: package, + observabilityScope: observabilityScope) + queue.async { + completion(.success(container)) + } } } catch { queue.async { diff --git a/Sources/Workspace/Workspace+State.swift b/Sources/Workspace/Workspace+State.swift index 0d70ee2af0c..784ae7f3aff 100644 --- a/Sources/Workspace/Workspace+State.swift +++ b/Sources/Workspace/Workspace+State.swift @@ -440,6 +440,9 @@ extension WorkspaceStateStorage { self.kind = .registry // FIXME: placeholder self.location = self.identity.description + case .providedLibrary(let path): + self.kind = .providedLibrary + self.location = path.pathString } self.name = reference.deprecatedName } @@ -450,6 +453,7 @@ extension WorkspaceStateStorage { case localSourceControl case remoteSourceControl case registry + case providedLibrary } } } @@ -492,6 +496,8 @@ extension PackageModel.PackageReference { kind = .remoteSourceControl(SourceControlURL(reference.location)) case .registry: kind = .registry(identity) + case .providedLibrary: + kind = try .providedLibrary(.init(validating: reference.location)) } self.init( @@ -766,6 +772,9 @@ extension WorkspaceStateStorage { self.kind = .registry // FIXME: placeholder self.location = self.identity.description + case .providedLibrary(let path): + self.kind = .providedLibrary + self.location = path.pathString } self.name = reference.deprecatedName } @@ -776,6 +785,7 @@ extension WorkspaceStateStorage { case localSourceControl case remoteSourceControl case registry + case providedLibrary } } } @@ -819,6 +829,8 @@ extension PackageModel.PackageReference { kind = .remoteSourceControl(SourceControlURL(reference.location)) case .registry: kind = .registry(identity) + case .providedLibrary: + kind = try .providedLibrary(.init(validating: reference.location)) } self.init( diff --git a/Sources/Workspace/Workspace.swift b/Sources/Workspace/Workspace.swift index 802bc1065c9..0f81611f1fe 100644 --- a/Sources/Workspace/Workspace.swift +++ b/Sources/Workspace/Workspace.swift @@ -1269,6 +1269,8 @@ extension Workspace { try self.removeRepository(dependency: dependencyToRemove) case .registry: try self.removeRegistryArchive(for: dependencyToRemove) + case .providedLibrary: + break // NOOP } // Save the state. diff --git a/Tests/PackageLoadingTests/PDLoadingTests.swift b/Tests/PackageLoadingTests/PDLoadingTests.swift index aa2bfd7af44..08c82442d63 100644 --- a/Tests/PackageLoadingTests/PDLoadingTests.swift +++ b/Tests/PackageLoadingTests/PDLoadingTests.swift @@ -97,6 +97,8 @@ class PackageDescriptionLoadingTests: XCTestCase, ManifestLoaderDelegate { packagePath = path case .remoteSourceControl, .registry: packagePath = .root + case .providedLibrary(let path): + packagePath = path } let toolsVersion = toolsVersion From b88cd882fc8c79a632009df2bd5e1dfc83cf4600 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 16 Apr 2024 11:30:48 -0700 Subject: [PATCH 107/159] Expanded `providedLibrary` PackageReference to include origin This is useful for identity comparisons to enable replacement library to match against original remote package. --- Sources/PackageGraph/ModulesGraph.swift | 5 +-- .../PackageLoading/ManifestJSONParser.swift | 2 +- Sources/PackageModel/IdentityResolver.swift | 4 +-- Sources/PackageModel/PackageReference.swift | 32 +++++++++++------ .../SPMTestSupport/MockManifestLoader.swift | 8 ++--- Sources/Workspace/Diagnostics.swift | 4 +-- Sources/Workspace/Workspace+State.swift | 34 ++++++++++++++++--- .../PackageLoadingTests/PDLoadingTests.swift | 2 +- 8 files changed, 64 insertions(+), 27 deletions(-) diff --git a/Sources/PackageGraph/ModulesGraph.swift b/Sources/PackageGraph/ModulesGraph.swift index ba29c9ef906..49b2ea1f979 100644 --- a/Sources/PackageGraph/ModulesGraph.swift +++ b/Sources/PackageGraph/ModulesGraph.swift @@ -358,11 +358,12 @@ extension PackageGraphError: CustomStringConvertible { switch $0.manifest.packageKind { case .root(let path), .fileSystem(let path), - .localSourceControl(let path), - .providedLibrary(let path): + .localSourceControl(let path): description += " (at '\(path)')" case .remoteSourceControl(let url): description += " (from '\(url)')" + case .providedLibrary(let url, let path): + description += " (from \(url) at \(path))" case .registry: break } diff --git a/Sources/PackageLoading/ManifestJSONParser.swift b/Sources/PackageLoading/ManifestJSONParser.swift index 7b95ae7ab93..3818c565828 100644 --- a/Sources/PackageLoading/ManifestJSONParser.swift +++ b/Sources/PackageLoading/ManifestJSONParser.swift @@ -83,7 +83,7 @@ enum ManifestJSONParser { case .localSourceControl(let _packagePath): // we have a more accurate path than the virtual one packagePath = _packagePath - case .root(let _packagePath), .fileSystem(let _packagePath), .providedLibrary(let _packagePath): + case .root(let _packagePath), .fileSystem(let _packagePath), .providedLibrary(_, let _packagePath): // we dont have a more accurate path, and they should be the same // asserting (debug only) to make sure refactoring is correct 11/2023 assert(packagePath == _packagePath, "expecting package path '\(packagePath)' to be the same as '\(_packagePath)'") diff --git a/Sources/PackageModel/IdentityResolver.swift b/Sources/PackageModel/IdentityResolver.swift index 63bc0b560c9..9755d7e26f3 100644 --- a/Sources/PackageModel/IdentityResolver.swift +++ b/Sources/PackageModel/IdentityResolver.swift @@ -46,8 +46,8 @@ public struct DefaultIdentityResolver: IdentityResolver { return try self.resolveIdentity(for: url) case .registry(let identity): return identity - case .providedLibrary(let path): - return try self.resolveIdentity(for: path) + case .providedLibrary(let url, _): + return try self.resolveIdentity(for: url) } } diff --git a/Sources/PackageModel/PackageReference.swift b/Sources/PackageModel/PackageReference.swift index 448909fe03b..545a0f6ade8 100644 --- a/Sources/PackageModel/PackageReference.swift +++ b/Sources/PackageModel/PackageReference.swift @@ -34,8 +34,8 @@ public struct PackageReference { /// A package from a registry. case registry(PackageIdentity) - /// A prebuilt library provided by a toolchain - case providedLibrary(AbsolutePath) + /// A prebuilt library provided by a toolchain for a package identified by the given "origin" URL. + case providedLibrary(SourceControlURL, AbsolutePath) // FIXME: we should not need this once we migrate off URLs //@available(*, deprecated) @@ -52,8 +52,8 @@ public struct PackageReference { case .registry(let identity): // FIXME: this is a placeholder return identity.description - case .providedLibrary(let path): - return path.pathString + case .providedLibrary(let url, _): + return url.absoluteString } } @@ -75,8 +75,8 @@ public struct PackageReference { return "remoteSourceControl \(url)" case .registry(let identity): return "registry \(identity)" - case .providedLibrary(let path): - return "library \(path)" + case .providedLibrary(let url, let path): + return "provided library for \(url) @ \(path)" } } @@ -131,8 +131,8 @@ public struct PackageReference { case .registry(let identity): // FIXME: this is a placeholder self.deprecatedName = name ?? identity.description - case .providedLibrary(let path): - self.deprecatedName = name ?? PackageIdentityParser.computeDefaultName(fromPath: path) + case .providedLibrary(let url, _): + self.deprecatedName = name ?? PackageIdentityParser.computeDefaultName(fromURL: url) } } @@ -161,8 +161,12 @@ public struct PackageReference { PackageReference(identity: identity, kind: .registry(identity)) } - public static func library(identity: PackageIdentity, path: AbsolutePath) -> PackageReference { - PackageReference(identity: identity, kind: .providedLibrary(path)) + public static func providedLibrary( + identity: PackageIdentity, + origin: SourceControlURL, + path: AbsolutePath + ) -> PackageReference { + PackageReference(identity: identity, kind: .providedLibrary(origin, path)) } } @@ -183,6 +187,11 @@ extension PackageReference: Equatable { switch (self.kind, other.kind) { case (.remoteSourceControl(let lurl), .remoteSourceControl(let rurl)): return lurl.canonicalURL == rurl.canonicalURL + case (.remoteSourceControl(let lurl), .providedLibrary(let rurl, _)), + (.providedLibrary(let lurl, _), .remoteSourceControl(let rurl)): + return lurl.canonicalURL == rurl.canonicalURL + case (.providedLibrary(_, let lpath), .providedLibrary(_, let rpath)): + return lpath == rpath default: return true } @@ -237,8 +246,9 @@ extension PackageReference.Kind: Encodable { case .registry: var unkeyedContainer = container.nestedUnkeyedContainer(forKey: .registry) try unkeyedContainer.encode(self.isRoot) - case .providedLibrary(let path): + case .providedLibrary(let url, let path): var unkeyedContainer = container.nestedUnkeyedContainer(forKey: .providedLibrary) + try unkeyedContainer.encode(url) try unkeyedContainer.encode(path) } } diff --git a/Sources/SPMTestSupport/MockManifestLoader.swift b/Sources/SPMTestSupport/MockManifestLoader.swift index 08b1bf32540..70da34b5cc6 100644 --- a/Sources/SPMTestSupport/MockManifestLoader.swift +++ b/Sources/SPMTestSupport/MockManifestLoader.swift @@ -109,8 +109,8 @@ extension ManifestLoader { packageIdentity = identity // FIXME: placeholder packageLocation = identity.description - case .providedLibrary(let path): - packageIdentity = try identityResolver.resolveIdentity(for: path) + case .providedLibrary(let url, let path): + packageIdentity = try identityResolver.resolveIdentity(for: url) packageLocation = path.pathString } return try await self.load( @@ -159,8 +159,8 @@ extension ManifestLoader { packageIdentity = identity // FIXME: placeholder packageLocation = identity.description - case .providedLibrary(let path): - packageIdentity = try identityResolver.resolveIdentity(for: path) + case .providedLibrary(let url, let path): + packageIdentity = try identityResolver.resolveIdentity(for: url) packageLocation = path.pathString } return try await self.load( diff --git a/Sources/Workspace/Diagnostics.swift b/Sources/Workspace/Diagnostics.swift index 007214647d1..aef1442693b 100644 --- a/Sources/Workspace/Diagnostics.swift +++ b/Sources/Workspace/Diagnostics.swift @@ -183,9 +183,9 @@ extension Basics.Diagnostic { switch $0.kind { case .registry(let identity): return "'\(identity.description)'" - case .remoteSourceControl(let url): + case .remoteSourceControl(let url), .providedLibrary(let url, _): return "'\($0.identity)' from \(url)" - case .localSourceControl(let path), .fileSystem(let path), .root(let path), .providedLibrary(let path): + case .localSourceControl(let path), .fileSystem(let path), .root(let path): return "'\($0.identity)' at \(path)" } } diff --git a/Sources/Workspace/Workspace+State.swift b/Sources/Workspace/Workspace+State.swift index 784ae7f3aff..bd4db386e2e 100644 --- a/Sources/Workspace/Workspace+State.swift +++ b/Sources/Workspace/Workspace+State.swift @@ -420,6 +420,7 @@ extension WorkspaceStateStorage { let kind: Kind let location: String let name: String + let originURL: String? init(_ reference: PackageModel.PackageReference) { self.identity = reference.identity.description @@ -427,21 +428,27 @@ extension WorkspaceStateStorage { case .root(let path): self.kind = .root self.location = path.pathString + self.originURL = nil case .fileSystem(let path): self.kind = .fileSystem self.location = path.pathString + self.originURL = nil case .localSourceControl(let path): self.kind = .localSourceControl self.location = path.pathString + self.originURL = nil case .remoteSourceControl(let url): self.kind = .remoteSourceControl self.location = url.absoluteString + self.originURL = nil case .registry: self.kind = .registry // FIXME: placeholder self.location = self.identity.description - case .providedLibrary(let path): + self.originURL = nil + case .providedLibrary(let url, let path): self.kind = .providedLibrary + self.originURL = url.absoluteString self.location = path.pathString } self.name = reference.deprecatedName @@ -497,7 +504,13 @@ extension PackageModel.PackageReference { case .registry: kind = .registry(identity) case .providedLibrary: - kind = try .providedLibrary(.init(validating: reference.location)) + guard let url = reference.originURL else { + throw InternalError("Cannot form provided library reference without origin: \(reference)") + } + kind = try .providedLibrary( + SourceControlURL(url), + .init(validating: reference.location) + ) } self.init( @@ -752,6 +765,7 @@ extension WorkspaceStateStorage { let kind: Kind let location: String let name: String + let originURL: String? init(_ reference: PackageModel.PackageReference) { self.identity = reference.identity.description @@ -759,21 +773,27 @@ extension WorkspaceStateStorage { case .root(let path): self.kind = .root self.location = path.pathString + self.originURL = nil case .fileSystem(let path): self.kind = .fileSystem self.location = path.pathString + self.originURL = nil case .localSourceControl(let path): self.kind = .localSourceControl self.location = path.pathString + self.originURL = nil case .remoteSourceControl(let url): self.kind = .remoteSourceControl self.location = url.absoluteString + self.originURL = nil case .registry: self.kind = .registry // FIXME: placeholder self.location = self.identity.description - case .providedLibrary(let path): + self.originURL = nil + case .providedLibrary(let url, let path): self.kind = .providedLibrary + self.originURL = url.absoluteString self.location = path.pathString } self.name = reference.deprecatedName @@ -830,7 +850,13 @@ extension PackageModel.PackageReference { case .registry: kind = .registry(identity) case .providedLibrary: - kind = try .providedLibrary(.init(validating: reference.location)) + guard let url = reference.originURL else { + throw InternalError("Cannot form a provided library reference without origin: \(reference)") + } + kind = try .providedLibrary( + SourceControlURL(url), + .init(validating: reference.location) + ) } self.init( diff --git a/Tests/PackageLoadingTests/PDLoadingTests.swift b/Tests/PackageLoadingTests/PDLoadingTests.swift index 08c82442d63..b8b04ef0e91 100644 --- a/Tests/PackageLoadingTests/PDLoadingTests.swift +++ b/Tests/PackageLoadingTests/PDLoadingTests.swift @@ -97,7 +97,7 @@ class PackageDescriptionLoadingTests: XCTestCase, ManifestLoaderDelegate { packagePath = path case .remoteSourceControl, .registry: packagePath = .root - case .providedLibrary(let path): + case .providedLibrary(_, let path): packagePath = path } From 5aa48354e1496e23b94d1dc700e3f02e88ae8a6f Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 16 Apr 2024 13:28:26 -0700 Subject: [PATCH 108/159] Augment ManagedDependency to handle provided libraries --- .../Plugins/PluginContextSerializer.swift | 2 +- Sources/Workspace/ManagedDependency.swift | 20 ++++++++++++++ .../Workspace/Workspace+Dependencies.swift | 24 ++++++++++++++--- Sources/Workspace/Workspace+Editing.swift | 3 +++ Sources/Workspace/Workspace+Manifests.swift | 15 ++++++++--- Sources/Workspace/Workspace+Pinning.swift | 2 +- Sources/Workspace/Workspace+State.swift | 26 +++++++++++++++++++ Sources/Workspace/Workspace.swift | 4 +++ 8 files changed, 88 insertions(+), 8 deletions(-) diff --git a/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift b/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift index 3816f7791b5..e246f2300ef 100644 --- a/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift +++ b/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift @@ -240,7 +240,7 @@ internal struct PluginContextSerializer { return .repository(url: url.absoluteString, displayVersion: String(describing: package.manifest.version), scmRevision: String(describing: package.manifest.revision)) case .registry(let identity): return .registry(identity: identity.description, displayVersion: String(describing: package.manifest.version)) - case .providedLibrary(_): + case .providedLibrary(_, _): throw InternalError("provided libraries are not supported in plugin context") } } diff --git a/Sources/Workspace/ManagedDependency.swift b/Sources/Workspace/ManagedDependency.swift index b771f37221c..4f476c99965 100644 --- a/Sources/Workspace/ManagedDependency.swift +++ b/Sources/Workspace/ManagedDependency.swift @@ -34,6 +34,9 @@ extension Workspace { /// The dependency is downloaded from a registry. case registryDownload(version: Version) + /// The dependency is part of the toolchain in a binary form. + case providedLibrary(at: AbsolutePath, version: Version) + /// The dependency is in edited state. /// /// If the path is non-nil, the dependency is managed by a user and is @@ -51,6 +54,8 @@ extension Workspace { return "sourceControlCheckout (\(checkoutState))" case .registryDownload(let version): return "registryDownload (\(version))" + case .providedLibrary(let path, let version): + return "library (\(path) @ \(version)" case .edited: return "edited" case .custom: @@ -146,6 +151,21 @@ extension Workspace { ) } + public static func providedLibrary( + packageRef: PackageReference, + version: Version + ) throws -> ManagedDependency { + guard case .providedLibrary(_, let path) = packageRef.kind else { + throw InternalError("invalid package type: \(packageRef.kind)") + } + + return ManagedDependency( + packageRef: packageRef, + state: .providedLibrary(at: path, version: version), + subpath: try RelativePath(validating: packageRef.identity.description) + ) + } + /// Create an edited dependency public static func edited( packageRef: PackageReference, diff --git a/Sources/Workspace/Workspace+Dependencies.swift b/Sources/Workspace/Workspace+Dependencies.swift index 5ece297dc4a..9609e14ecc1 100644 --- a/Sources/Workspace/Workspace+Dependencies.swift +++ b/Sources/Workspace/Workspace+Dependencies.swift @@ -430,7 +430,7 @@ extension Workspace { return !pin.state.equals(checkoutState) case .registryDownload(let version): return !pin.state.equals(version) - case .edited, .fileSystem, .custom: + case .edited, .fileSystem, .providedLibrary, .custom: return true } } @@ -781,6 +781,19 @@ extension Workspace { state: .custom(version: version, path: path), subpath: RelativePath(validating: "") ) + self.state.dependencies.add(dependency) + try self.state.save() + return path + } else if let libraryContainer = container as? ProvidedLibraryPackageContainer { + guard case .providedLibrary(_, let path) = libraryContainer.package.kind else { + throw InternalError("invalid container for \(package.identity) of type \(package.kind)") + } + + let dependency: ManagedDependency = try .providedLibrary( + packageRef: libraryContainer.package, + version: version + ) + self.state.dependencies.add(dependency) try self.state.save() return path @@ -1033,6 +1046,8 @@ extension Workspace { packageStateChanges[binding.package.identity] = (binding.package, .updated(newState)) case .registryDownload: throw InternalError("Unexpected unversioned binding for downloaded dependency") + case .providedLibrary: + throw InternalError("Unexpected unversioned binding for library dependency") case .custom: throw InternalError("Unexpected unversioned binding for custom dependency") } @@ -1103,9 +1118,12 @@ extension Workspace { case .version(let version): let stateChange: PackageStateChange switch currentDependency?.state { - case .sourceControlCheckout(.version(version, _)), .registryDownload(version), .custom(version, _): + case .sourceControlCheckout(.version(version, _)), + .registryDownload(version), + .providedLibrary(_, version: version), + .custom(version, _): stateChange = .unchanged - case .edited, .fileSystem, .sourceControlCheckout, .registryDownload, .custom: + case .edited, .fileSystem, .sourceControlCheckout, .registryDownload, .providedLibrary, .custom: stateChange = .updated(.init(requirement: .version(version), products: binding.products)) case nil: stateChange = .added(.init(requirement: .version(version), products: binding.products)) diff --git a/Sources/Workspace/Workspace+Editing.swift b/Sources/Workspace/Workspace+Editing.swift index c27afe324a6..86f7d085586 100644 --- a/Sources/Workspace/Workspace+Editing.swift +++ b/Sources/Workspace/Workspace+Editing.swift @@ -52,6 +52,9 @@ extension Workspace { case .registryDownload: observabilityScope.emit(error: "registry dependency '\(dependency.packageRef.identity)' can't be edited") return + case .providedLibrary: + observabilityScope.emit(error: "library dependency '\(dependency.packageRef.identity)' can't be edited") + return case .custom: observabilityScope.emit(error: "custom dependency '\(dependency.packageRef.identity)' can't be edited") return diff --git a/Sources/Workspace/Workspace+Manifests.swift b/Sources/Workspace/Workspace+Manifests.swift index e202a624e51..48a229a9c85 100644 --- a/Sources/Workspace/Workspace+Manifests.swift +++ b/Sources/Workspace/Workspace+Manifests.swift @@ -151,7 +151,7 @@ extension Workspace { result.insert(packageRef) } - case .registryDownload, .edited, .custom: + case .registryDownload, .edited, .providedLibrary, .custom: continue case .fileSystem: result.insert(dependency.packageRef) @@ -343,7 +343,7 @@ extension Workspace { products: productFilter ) allConstraints.append(constraint) - case .sourceControlCheckout, .registryDownload, .fileSystem, .custom: + case .sourceControlCheckout, .registryDownload, .fileSystem, .providedLibrary, .custom: break } allConstraints += try externalManifest.dependencyConstraints(productFilter: productFilter) @@ -358,7 +358,7 @@ extension Workspace { for (_, managedDependency, productFilter, _) in dependencies { switch managedDependency.state { - case .sourceControlCheckout, .registryDownload, .fileSystem, .custom: continue + case .sourceControlCheckout, .registryDownload, .fileSystem, .providedLibrary, .custom: continue case .edited: break } // FIXME: We shouldn't need to construct a new package reference object here. @@ -394,6 +394,8 @@ extension Workspace { return path ?? self.location.editSubdirectory(for: dependency) case .fileSystem(let path): return path + case .providedLibrary(let path, _): + return path case .custom(_, let path): return path } @@ -684,6 +686,9 @@ extension Workspace { case .registryDownload(let downloadedVersion): packageKind = managedDependency.packageRef.kind packageVersion = downloadedVersion + case .providedLibrary(_, let version): + packageKind = managedDependency.packageRef.kind + packageVersion = version case .custom(let availableVersion, _): packageKind = managedDependency.packageRef.kind packageVersion = availableVersion @@ -897,6 +902,10 @@ extension Workspace { observabilityScope .emit(.editedDependencyMissing(packageName: dependency.packageRef.identity.description)) + case .providedLibrary(_, version: _): + // TODO: If the dependency is not available we can turn it into a source control dependency + break + case .fileSystem: self.state.dependencies.remove(dependency.packageRef.identity) try self.state.save() diff --git a/Sources/Workspace/Workspace+Pinning.swift b/Sources/Workspace/Workspace+Pinning.swift index b51e69fb04f..6a652a937ad 100644 --- a/Sources/Workspace/Workspace+Pinning.swift +++ b/Sources/Workspace/Workspace+Pinning.swift @@ -145,7 +145,7 @@ extension PinsStore.Pin { packageRef: dependency.packageRef, state: .version(version, revision: .none) ) - case .edited, .fileSystem, .custom: + case .edited, .fileSystem, .providedLibrary, .custom: // NOOP return nil } diff --git a/Sources/Workspace/Workspace+State.swift b/Sources/Workspace/Workspace+State.swift index bd4db386e2e..8d827cb562f 100644 --- a/Sources/Workspace/Workspace+State.swift +++ b/Sources/Workspace/Workspace+State.swift @@ -256,6 +256,15 @@ extension WorkspaceStateStorage { let version = try container.decode(String.self, forKey: .version) return try self .init(underlying: .registryDownload(version: TSCUtility.Version(versionString: version))) + case "providedLibrary": + let path = try container.decode(AbsolutePath.self, forKey: .path) + let version = try container.decode(String.self, forKey: .version) + return try self.init( + underlying: .providedLibrary( + at: path, + version: TSCUtility.Version(versionString: version) + ) + ) case "edited": let path = try container.decode(AbsolutePath?.self, forKey: .path) return try self.init(underlying: .edited( @@ -286,6 +295,10 @@ extension WorkspaceStateStorage { case .registryDownload(let version): try container.encode("registryDownload", forKey: .name) try container.encode(version, forKey: .version) + case .providedLibrary(let path, let version): + try container.encode("providedLibrary", forKey: .name) + try container.encode(path, forKey: .path) + try container.encode(version, forKey: .version) case .edited(_, let path): try container.encode("edited", forKey: .name) try container.encode(path, forKey: .path) @@ -631,6 +644,15 @@ extension WorkspaceStateStorage { let version = try container.decode(String.self, forKey: .version) return try self .init(underlying: .registryDownload(version: TSCUtility.Version(versionString: version))) + case "providedLibrary": + let path = try container.decode(AbsolutePath.self, forKey: .path) + let version = try container.decode(String.self, forKey: .version) + return try self.init( + underlying: .providedLibrary( + at: path, + version: TSCUtility.Version(versionString: version) + ) + ) case "edited": let path = try container.decode(AbsolutePath?.self, forKey: .path) return try self.init(underlying: .edited( @@ -661,6 +683,10 @@ extension WorkspaceStateStorage { case .registryDownload(let version): try container.encode("registryDownload", forKey: .name) try container.encode(version, forKey: .version) + case .providedLibrary(let path, let version): + try container.encode("providedLibrary", forKey: .name) + try container.encode(path, forKey: .path) + try container.encode(version, forKey: .version) case .edited(_, let path): try container.encode("edited", forKey: .name) try container.encode(path, forKey: .path) diff --git a/Sources/Workspace/Workspace.swift b/Sources/Workspace/Workspace.swift index 0f81611f1fe..5574916885c 100644 --- a/Sources/Workspace/Workspace.swift +++ b/Sources/Workspace/Workspace.swift @@ -708,6 +708,8 @@ extension Workspace { defaultRequirement = checkoutState.requirement case .registryDownload(let version), .custom(let version, _): defaultRequirement = .versionSet(.exact(version)) + case .providedLibrary(_, version: let version): + defaultRequirement = .versionSet(.exact(version)) case .fileSystem: throw StringError("local dependency '\(dependency.packageRef.identity)' can't be resolved") case .edited: @@ -1351,6 +1353,8 @@ extension Workspace { } case .registryDownload(let version)?, .custom(let version, _): result.append("resolved to '\(version)'") + case .providedLibrary(_, version: let version): + result.append("resolved to '\(version)'") case .edited?: result.append("edited") case .fileSystem?: From 3eb69105f571ff5685c5f50084c73e639a8c4190 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 17 Apr 2024 15:13:49 -0700 Subject: [PATCH 109/159] [PackageModel] Add a new target kind - provided library This target points to a prebuilt library that comes from a certain location in a toolchain. It's going to be injected by the package manager into library manifests and shouldn't be accessible via user-facing APIs. --- .../ProductBuildDescription.swift | 5 +++ .../LLBuildManifestBuilder+Swift.swift | 2 + .../Build/BuildPlan/BuildPlan+Product.swift | 8 +++- Sources/Build/BuildPlan/BuildPlan+Swift.swift | 5 +++ Sources/Build/BuildPlan/BuildPlan.swift | 2 +- Sources/Commands/Snippets/Cards/TopCard.swift | 2 + Sources/PackageLoading/PackageBuilder.swift | 15 +++++++ Sources/PackageModel/CMakeLists.txt | 1 + .../Manifest/TargetDescription.swift | 19 +++++++- .../ManifestSourceGeneration.swift | 2 + .../Target/ProvidedLibraryTarget.swift | 44 +++++++++++++++++++ Sources/PackageModel/Target/Target.swift | 2 + Sources/PackageModelSyntax/AddTarget.swift | 4 +- .../TargetDescription+Syntax.swift | 1 + .../Plugins/PluginContextSerializer.swift | 2 +- Sources/XCBuildSupport/PIFBuilder.swift | 3 ++ 16 files changed, 111 insertions(+), 6 deletions(-) create mode 100644 Sources/PackageModel/Target/ProvidedLibraryTarget.swift diff --git a/Sources/Build/BuildDescription/ProductBuildDescription.swift b/Sources/Build/BuildDescription/ProductBuildDescription.swift index 2277a6f638f..289c48dec48 100644 --- a/Sources/Build/BuildDescription/ProductBuildDescription.swift +++ b/Sources/Build/BuildDescription/ProductBuildDescription.swift @@ -45,6 +45,9 @@ package final class ProductBuildDescription: SPMBuildCore.ProductBuildDescriptio // Computed during build planning. var dylibs: [ProductBuildDescription] = [] + /// The list of provided libraries that are going to be used by this product. + var providedLibraries: [String: AbsolutePath] = [:] + /// Any additional flags to be added. These flags are expected to be computed during build planning. var additionalFlags: [String] = [] @@ -156,6 +159,8 @@ package final class ProductBuildDescription: SPMBuildCore.ProductBuildDescriptio args += ["-F", self.buildParameters.buildPath.pathString] } + self.providedLibraries.forEach { args += ["-L", $1.pathString, "-l", $0] } + args += ["-L", self.buildParameters.buildPath.pathString] args += try ["-o", binaryPath.pathString] args += ["-module-name", self.product.name.spm_mangledToC99ExtendedIdentifier()] diff --git a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift index 10fa290f50d..e2ea4abc021 100644 --- a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift +++ b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift @@ -424,6 +424,8 @@ extension LLBuildManifestBuilder { if target.underlying is BinaryTarget { return } // Ignore Plugin Targets. if target.underlying is PluginTarget { return } + // Ignore Provided Libraries. + if target.underlying is ProvidedLibraryTarget { return } // Depend on the binary for executable targets. if target.type == .executable { diff --git a/Sources/Build/BuildPlan/BuildPlan+Product.swift b/Sources/Build/BuildPlan/BuildPlan+Product.swift index c3932f0d626..c1731786e1d 100644 --- a/Sources/Build/BuildPlan/BuildPlan+Product.swift +++ b/Sources/Build/BuildPlan/BuildPlan+Product.swift @@ -115,6 +115,8 @@ extension BuildPlan { } buildProduct.libraryBinaryPaths = dependencies.libraryBinaryPaths + buildProduct.providedLibraries = dependencies.providedLibraries + buildProduct.availableTools = dependencies.availableTools } @@ -127,6 +129,7 @@ extension BuildPlan { staticTargets: [ResolvedModule], systemModules: [ResolvedModule], libraryBinaryPaths: Set, + providedLibraries: [String: AbsolutePath], availableTools: [String: AbsolutePath] ) { /* Prior to tools-version 5.9, we used to erroneously recursively traverse executable/plugin dependencies and statically include their @@ -204,6 +207,7 @@ extension BuildPlan { var staticTargets = [ResolvedModule]() var systemModules = [ResolvedModule]() var libraryBinaryPaths: Set = [] + var providedLibraries = [String: AbsolutePath]() var availableTools = [String: AbsolutePath]() for dependency in allTargets { @@ -257,6 +261,8 @@ extension BuildPlan { } case .plugin: continue + case .providedLibrary: + providedLibraries[target.name] = target.underlying.path } case .product(let product, _): @@ -274,7 +280,7 @@ extension BuildPlan { } } - return (linkLibraries, staticTargets, systemModules, libraryBinaryPaths, availableTools) + return (linkLibraries, staticTargets, systemModules, libraryBinaryPaths, providedLibraries, availableTools) } /// Extracts the artifacts from an artifactsArchive diff --git a/Sources/Build/BuildPlan/BuildPlan+Swift.swift b/Sources/Build/BuildPlan/BuildPlan+Swift.swift index 058631598f2..dadb3c776e8 100644 --- a/Sources/Build/BuildPlan/BuildPlan+Swift.swift +++ b/Sources/Build/BuildPlan/BuildPlan+Swift.swift @@ -14,6 +14,7 @@ import struct Basics.InternalError import class PackageModel.BinaryTarget import class PackageModel.ClangTarget import class PackageModel.SystemLibraryTarget +import class PackageModel.ProvidedLibraryTarget extension BuildPlan { func plan(swiftTarget: SwiftTargetBuildDescription) throws { @@ -48,6 +49,10 @@ extension BuildPlan { swiftTarget.libraryBinaryPaths.insert(library.libraryPath) } } + case let target as ProvidedLibraryTarget: + swiftTarget.additionalFlags += [ + "-I", target.path.pathString + ] default: break } diff --git a/Sources/Build/BuildPlan/BuildPlan.swift b/Sources/Build/BuildPlan/BuildPlan.swift index 123d34e3e55..7a74eb5cf3f 100644 --- a/Sources/Build/BuildPlan/BuildPlan.swift +++ b/Sources/Build/BuildPlan/BuildPlan.swift @@ -433,7 +433,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan { toolsVersion: toolsVersion, fileSystem: fileSystem )) - case is SystemLibraryTarget, is BinaryTarget: + case is SystemLibraryTarget, is BinaryTarget, is ProvidedLibraryTarget: break default: throw InternalError("unhandled \(target.underlying)") diff --git a/Sources/Commands/Snippets/Cards/TopCard.swift b/Sources/Commands/Snippets/Cards/TopCard.swift index 7b915234db0..bb0398fda1e 100644 --- a/Sources/Commands/Snippets/Cards/TopCard.swift +++ b/Sources/Commands/Snippets/Cards/TopCard.swift @@ -153,6 +153,8 @@ fileprivate extension Target.Kind { return "snippets" case .macro: return "macros" + case .providedLibrary: + return "provided libraries" } } } diff --git a/Sources/PackageLoading/PackageBuilder.swift b/Sources/PackageLoading/PackageBuilder.swift index 488a25cb0a2..23d1a4d2fbf 100644 --- a/Sources/PackageLoading/PackageBuilder.swift +++ b/Sources/PackageLoading/PackageBuilder.swift @@ -542,6 +542,16 @@ public final class PackageBuilder { throw ModuleError.artifactNotFound(targetName: target.name, expectedArtifactName: target.name) } return artifact.path + } else if let targetPath = target.path, target.type == .providedLibrary { + guard let path = try? AbsolutePath(validating: targetPath) else { + throw ModuleError.invalidCustomPath(target: target.name, path: targetPath) + } + + if !self.fileSystem.isDirectory(path) { + throw ModuleError.unsupportedTargetPath(targetPath) + } + + return path } else if let subpath = target.path { // If there is a custom path defined, use that. if subpath == "" || subpath == "." { return self.packagePath @@ -855,6 +865,11 @@ public final class PackageBuilder { path: potentialModule.path, origin: artifactOrigin ) + } else if potentialModule.type == .providedLibrary { + return ProvidedLibraryTarget( + name: potentialModule.name, + path: potentialModule.path + ) } // Check for duplicate target dependencies diff --git a/Sources/PackageModel/CMakeLists.txt b/Sources/PackageModel/CMakeLists.txt index 26a19962746..a7b13798bb4 100644 --- a/Sources/PackageModel/CMakeLists.txt +++ b/Sources/PackageModel/CMakeLists.txt @@ -51,6 +51,7 @@ add_library(PackageModel Target/BinaryTarget.swift Target/ClangTarget.swift Target/PluginTarget.swift + Target/ProvidedLibraryTarget.swift Target/SwiftTarget.swift Target/SystemLibraryTarget.swift Target/Target.swift diff --git a/Sources/PackageModel/Manifest/TargetDescription.swift b/Sources/PackageModel/Manifest/TargetDescription.swift index 57df7f6d37a..1c943a5313c 100644 --- a/Sources/PackageModel/Manifest/TargetDescription.swift +++ b/Sources/PackageModel/Manifest/TargetDescription.swift @@ -21,6 +21,7 @@ public struct TargetDescription: Hashable, Encodable, Sendable { case binary case plugin case `macro` + case providedLibrary } /// Represents a target's dependency on another entity. @@ -222,6 +223,19 @@ public struct TargetDescription: Hashable, Encodable, Sendable { if pkgConfig != nil { throw Error.disallowedPropertyInTarget(targetName: name, propertyName: "pkgConfig") } if providers != nil { throw Error.disallowedPropertyInTarget(targetName: name, propertyName: "providers") } if pluginCapability != nil { throw Error.disallowedPropertyInTarget(targetName: name, propertyName: "pluginCapability") } + case .providedLibrary: + if path == nil { throw Error.providedLibraryTargetRequiresPath(targetName: name) } + if url != nil { throw Error.disallowedPropertyInTarget(targetName: name, propertyName: "url") } + if !dependencies.isEmpty { throw Error.disallowedPropertyInTarget(targetName: name, propertyName: "dependencies") } + if !exclude.isEmpty { throw Error.disallowedPropertyInTarget(targetName: name, propertyName: "exclude") } + if sources != nil { throw Error.disallowedPropertyInTarget(targetName: name, propertyName: "sources") } + if !resources.isEmpty { throw Error.disallowedPropertyInTarget(targetName: name, propertyName: "resources") } + if publicHeadersPath != nil { throw Error.disallowedPropertyInTarget(targetName: name, propertyName: "publicHeadersPath") } + if pkgConfig != nil { throw Error.disallowedPropertyInTarget(targetName: name, propertyName: "pkgConfig") } + if providers != nil { throw Error.disallowedPropertyInTarget(targetName: name, propertyName: "providers") } + if pluginCapability != nil { throw Error.disallowedPropertyInTarget(targetName: name, propertyName: "pluginCapability") } + if !settings.isEmpty { throw Error.disallowedPropertyInTarget(targetName: name, propertyName: "settings") } + if pluginUsages != nil { throw Error.disallowedPropertyInTarget(targetName: name, propertyName: "pluginUsages") } } self.name = name @@ -370,13 +384,16 @@ import protocol Foundation.LocalizedError private enum Error: LocalizedError, Equatable { case binaryTargetRequiresEitherPathOrURL(targetName: String) case disallowedPropertyInTarget(targetName: String, propertyName: String) - + case providedLibraryTargetRequiresPath(targetName: String) + var errorDescription: String? { switch self { case .binaryTargetRequiresEitherPathOrURL(let targetName): return "binary target '\(targetName)' neither defines neither path nor URL for its artifacts" case .disallowedPropertyInTarget(let targetName, let propertyName): return "target '\(targetName)' contains a value for disallowed property '\(propertyName)'" + case .providedLibraryTargetRequiresPath(let targetName): + return "provided library target '\(targetName)' does not define a path to the library" } } } diff --git a/Sources/PackageModel/ManifestSourceGeneration.swift b/Sources/PackageModel/ManifestSourceGeneration.swift index f2b0d65c45c..211c44032ee 100644 --- a/Sources/PackageModel/ManifestSourceGeneration.swift +++ b/Sources/PackageModel/ManifestSourceGeneration.swift @@ -317,6 +317,8 @@ fileprivate extension SourceCodeFragment { self.init(enum: "plugin", subnodes: params, multiline: true) case .macro: self.init(enum: "macro", subnodes: params, multiline: true) + case .providedLibrary: + self.init(enum: "providedLibrary", subnodes: params, multiline: true) } } diff --git a/Sources/PackageModel/Target/ProvidedLibraryTarget.swift b/Sources/PackageModel/Target/ProvidedLibraryTarget.swift new file mode 100644 index 00000000000..a8ed95b23ae --- /dev/null +++ b/Sources/PackageModel/Target/ProvidedLibraryTarget.swift @@ -0,0 +1,44 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import struct Basics.AbsolutePath + +/// Represents a target library that comes from a toolchain in prebuilt form. +public final class ProvidedLibraryTarget: Target { + public init( + name: String, + path: AbsolutePath + ) { + let sources = Sources(paths: [], root: path) + super.init( + name: name, + type: .providedLibrary, + path: sources.root, + sources: sources, + dependencies: [], + packageAccess: false, + buildSettings: .init(), + buildSettingsDescription: [], + pluginUsages: [], + usesUnsafeFlags: false + ) + } + + + public override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + } + + required public init(from decoder: Decoder) throws { + try super.init(from: decoder) + } +} diff --git a/Sources/PackageModel/Target/Target.swift b/Sources/PackageModel/Target/Target.swift index 40bb1a575e9..1074e353c87 100644 --- a/Sources/PackageModel/Target/Target.swift +++ b/Sources/PackageModel/Target/Target.swift @@ -21,6 +21,7 @@ public class Target: PolymorphicCodableProtocol { SystemLibraryTarget.self, BinaryTarget.self, PluginTarget.self, + ProvidedLibraryTarget.self, ] /// The target kind. @@ -33,6 +34,7 @@ public class Target: PolymorphicCodableProtocol { case plugin case snippet case `macro` + case providedLibrary } /// A group a target belongs to that allows customizing access boundaries. A target is treated as diff --git a/Sources/PackageModelSyntax/AddTarget.swift b/Sources/PackageModelSyntax/AddTarget.swift index 0ae83b78e0c..7fda6f58229 100644 --- a/Sources/PackageModelSyntax/AddTarget.swift +++ b/Sources/PackageModelSyntax/AddTarget.swift @@ -62,7 +62,7 @@ public struct AddTarget { ) let outerDirectory: String? = switch target.type { - case .binary, .plugin, .system: nil + case .binary, .plugin, .system, .providedLibrary: nil case .executable, .regular, .macro: "Sources" case .test: "Tests" } @@ -167,7 +167,7 @@ public struct AddTarget { } let sourceFileText: SourceFileSyntax = switch target.type { - case .binary, .plugin, .system: + case .binary, .plugin, .system, .providedLibrary: fatalError("should have exited above") case .macro: diff --git a/Sources/PackageModelSyntax/TargetDescription+Syntax.swift b/Sources/PackageModelSyntax/TargetDescription+Syntax.swift index f47f6590f06..5081932bed8 100644 --- a/Sources/PackageModelSyntax/TargetDescription+Syntax.swift +++ b/Sources/PackageModelSyntax/TargetDescription+Syntax.swift @@ -27,6 +27,7 @@ extension TargetDescription: ManifestSyntaxRepresentable { case .regular: "target" case .system: "systemLibrary" case .test: "testTarget" + case .providedLibrary: "providedLibrary" } } diff --git a/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift b/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift index e246f2300ef..9f84bbf9598 100644 --- a/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift +++ b/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift @@ -282,7 +282,7 @@ fileprivate extension WireInput.Target.TargetInfo.SourceModuleKind { self = .test case .macro: self = .macro - case .binary, .plugin, .systemModule: + case .binary, .plugin, .systemModule, .providedLibrary: throw StringError("unexpected target kind \(kind) for source module") } } diff --git a/Sources/XCBuildSupport/PIFBuilder.swift b/Sources/XCBuildSupport/PIFBuilder.swift index 0790e336238..6907cf4af38 100644 --- a/Sources/XCBuildSupport/PIFBuilder.swift +++ b/Sources/XCBuildSupport/PIFBuilder.swift @@ -399,6 +399,9 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { case .macro: // Macros are not supported when using XCBuild, similar to package plugins. return + case .providedLibrary: + // Provided libraries don't need to be built. + return } } From fd7995e8e05e8cc07438a41b49c7a71b2a333a9b Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 18 Apr 2024 11:34:55 -0700 Subject: [PATCH 110/159] Package LibraryMetadata together with its toolchain location Location is required down the line for build planning to inject proper header and library includes into the build. The location is currently assumed to be in `share/pm/` --- Sources/Build/BuildOperation.swift | 6 +-- .../PubGrub/ContainerProvider.swift | 2 +- .../PubGrub/PubGrubDependencyResolver.swift | 10 ++-- .../LibraryMetadata.swift | 11 ++++- Sources/PackageModel/Toolchain.swift | 2 +- Sources/PackageModel/UserToolchain.swift | 14 ++++-- .../SPMTestSupport/MockBuildTestHelper.swift | 2 +- .../Workspace/Workspace+Dependencies.swift | 16 +++---- Sources/Workspace/Workspace+Editing.swift | 4 +- Sources/Workspace/Workspace+Manifests.swift | 14 +++--- Sources/Workspace/Workspace.swift | 6 +-- Tests/BuildTests/BuildOperationTests.swift | 15 +++--- Tests/PackageGraphTests/PubgrubTests.swift | 47 +++++++++++-------- 13 files changed, 89 insertions(+), 60 deletions(-) diff --git a/Sources/Build/BuildOperation.swift b/Sources/Build/BuildOperation.swift index 9255948a749..6055c2c708d 100644 --- a/Sources/Build/BuildOperation.swift +++ b/Sources/Build/BuildOperation.swift @@ -269,7 +269,7 @@ package final class BuildOperation: PackageStructureDelegate, SPMBuildCore.Build // TODO: Currently this function will only match frameworks. func detectUnexpressedDependencies( - availableLibraries: [LibraryMetadata], + availableLibraries: [ProvidedLibrary], targetDependencyMap: [String: [String]]? ) { // Ensure we only emit these once, regardless of how many builds are being done. @@ -279,8 +279,8 @@ package final class BuildOperation: PackageStructureDelegate, SPMBuildCore.Build Self.didEmitUnexpressedDependencies = true let availableFrameworks = Dictionary(uniqueKeysWithValues: availableLibraries.compactMap { - if let identity = Set($0.identities.map(\.identity)).spm_only { - return ("\($0.productName!).framework", identity) + if let identity = Set($0.metadata.identities.map(\.identity)).spm_only { + return ("\($0.metadata.productName).framework", identity) } else { return nil } diff --git a/Sources/PackageGraph/Resolution/PubGrub/ContainerProvider.swift b/Sources/PackageGraph/Resolution/PubGrub/ContainerProvider.swift index d73e41ffdf9..46cd49a9fbf 100644 --- a/Sources/PackageGraph/Resolution/PubGrub/ContainerProvider.swift +++ b/Sources/PackageGraph/Resolution/PubGrub/ContainerProvider.swift @@ -107,7 +107,7 @@ final class ContainerProvider { } /// Starts prefetching the given containers. - func prefetch(containers identifiers: [PackageReference], availableLibraries: [LibraryMetadata]) { + func prefetch(containers identifiers: [PackageReference], availableLibraries: [ProvidedLibrary]) { let filteredIdentifiers = identifiers.filter { $0.matchingPrebuiltLibrary(in: availableLibraries) == nil } diff --git a/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift b/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift index 553b354790a..d6f5ddf3983 100644 --- a/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift +++ b/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift @@ -105,7 +105,7 @@ public struct PubGrubDependencyResolver { private let pins: PinsStore.Pins /// The packages that are available in a prebuilt form in SDK or a toolchain - private let availableLibraries: [LibraryMetadata] + private let availableLibraries: [ProvidedLibrary] /// The container provider used to load package containers. private let provider: ContainerProvider @@ -125,7 +125,7 @@ public struct PubGrubDependencyResolver { public init( provider: PackageContainerProvider, pins: PinsStore.Pins = [:], - availableLibraries: [LibraryMetadata] = [], + availableLibraries: [ProvidedLibrary] = [], skipDependenciesUpdates: Bool = false, prefetchBasedOnResolvedFile: Bool = false, observabilityScope: ObservabilityScope, @@ -895,14 +895,14 @@ extension PackageRequirement { } extension PackageReference { - public func matchingPrebuiltLibrary(in availableLibraries: [LibraryMetadata]) -> LibraryMetadata? { + public func matchingPrebuiltLibrary(in availableLibraries: [ProvidedLibrary]) -> ProvidedLibrary? { switch self.kind { case .fileSystem, .localSourceControl, .root, .providedLibrary: return nil // can never match a prebuilt library case .registry(let identity): if let registryIdentity = identity.registry { return availableLibraries.first( - where: { $0.identities.contains( + where: { $0.metadata.identities.contains( where: { $0 == .packageIdentity( scope: registryIdentity.scope.description, name: registryIdentity.name.description @@ -916,7 +916,7 @@ extension PackageReference { } case .remoteSourceControl(let url): return availableLibraries.first(where: { - $0.identities.contains(where: { $0 == .sourceControl(url: url) }) + $0.metadata.identities.contains(where: { $0 == .sourceControl(url: url) }) }) } } diff --git a/Sources/PackageModel/InstalledLibrariesSupport/LibraryMetadata.swift b/Sources/PackageModel/InstalledLibrariesSupport/LibraryMetadata.swift index 7e12d31a6f3..362dae238cf 100644 --- a/Sources/PackageModel/InstalledLibrariesSupport/LibraryMetadata.swift +++ b/Sources/PackageModel/InstalledLibrariesSupport/LibraryMetadata.swift @@ -13,6 +13,15 @@ import Basics import Foundation +public struct ProvidedLibrary { + public let location: AbsolutePath + public let metadata: LibraryMetadata + + public var version: String { + metadata.version + } +} + public struct LibraryMetadata: Decodable { public enum Identity: Equatable, Decodable { case packageIdentity(scope: String, name: String) @@ -24,7 +33,7 @@ public struct LibraryMetadata: Decodable { /// The version that was built (e.g., 509.0.2) public let version: String /// The product name, if it differs from the module name (e.g., SwiftParser). - public let productName: String? + public let productName: String let schemaVersion: Int } diff --git a/Sources/PackageModel/Toolchain.swift b/Sources/PackageModel/Toolchain.swift index cd7db9c68ba..8009f62d326 100644 --- a/Sources/PackageModel/Toolchain.swift +++ b/Sources/PackageModel/Toolchain.swift @@ -44,7 +44,7 @@ public protocol Toolchain { var installedSwiftPMConfiguration: InstalledSwiftPMConfiguration { get } /// Metadata for libraries provided by the used toolchain. - var providedLibraries: [LibraryMetadata] { get } + var providedLibraries: [ProvidedLibrary] { get } /// The root path to the Swift SDK used by this toolchain. var sdkRootPath: AbsolutePath? { get } diff --git a/Sources/PackageModel/UserToolchain.swift b/Sources/PackageModel/UserToolchain.swift index 9b7042332bb..7d1f833e903 100644 --- a/Sources/PackageModel/UserToolchain.swift +++ b/Sources/PackageModel/UserToolchain.swift @@ -88,7 +88,7 @@ public final class UserToolchain: Toolchain { public let installedSwiftPMConfiguration: InstalledSwiftPMConfiguration - public let providedLibraries: [LibraryMetadata] + public let providedLibraries: [ProvidedLibrary] /// Returns the runtime library for the given sanitizer. public func runtimeLibrary(for sanitizer: Sanitizer) throws -> AbsolutePath { @@ -487,7 +487,7 @@ public final class UserToolchain: Toolchain { searchStrategy: SearchStrategy = .default, customLibrariesLocation: ToolchainConfiguration.SwiftPMLibrariesLocation? = nil, customInstalledSwiftPMConfiguration: InstalledSwiftPMConfiguration? = nil, - customProvidedLibraries: [LibraryMetadata]? = nil + customProvidedLibraries: [ProvidedLibrary]? = nil ) throws { self.swiftSDK = swiftSDK self.environment = environment @@ -551,7 +551,15 @@ public final class UserToolchain: Toolchain { self.providedLibraries = try Self.loadJSONResource( config: path, type: [LibraryMetadata].self, - default: []) + default: [] + ).map { + .init( + location: path.parentDirectory.appending(component: $0.productName), + metadata: $0 + ) + }.filter { + localFileSystem.isDirectory($0.location) + } } // Use the triple from Swift SDK or compute the host triple using swiftc. diff --git a/Sources/SPMTestSupport/MockBuildTestHelper.swift b/Sources/SPMTestSupport/MockBuildTestHelper.swift index 1b8824b928c..9ce3ac5fa67 100644 --- a/Sources/SPMTestSupport/MockBuildTestHelper.swift +++ b/Sources/SPMTestSupport/MockBuildTestHelper.swift @@ -39,7 +39,7 @@ package struct MockToolchain: PackageModel.Toolchain { package let swiftPluginServerPath: AbsolutePath? = nil package let extraFlags = PackageModel.BuildFlags() package let installedSwiftPMConfiguration = InstalledSwiftPMConfiguration.default - package let providedLibraries = [LibraryMetadata]() + package let providedLibraries = [ProvidedLibrary]() package func getClangCompiler() throws -> AbsolutePath { "/fake/path/to/clang" diff --git a/Sources/Workspace/Workspace+Dependencies.swift b/Sources/Workspace/Workspace+Dependencies.swift index 9609e14ecc1..3f6a917cf07 100644 --- a/Sources/Workspace/Workspace+Dependencies.swift +++ b/Sources/Workspace/Workspace+Dependencies.swift @@ -37,7 +37,7 @@ import class PackageGraph.PinsStore import struct PackageGraph.PubGrubDependencyResolver import struct PackageGraph.Term import class PackageLoading.ManifestLoader -import struct PackageModel.LibraryMetadata +import struct PackageModel.ProvidedLibrary import enum PackageModel.PackageDependency import struct PackageModel.PackageIdentity import struct PackageModel.PackageReference @@ -57,7 +57,7 @@ extension Workspace { root: PackageGraphRootInput, packages: [String] = [], dryRun: Bool = false, - availableLibraries: [LibraryMetadata], + availableLibraries: [ProvidedLibrary], observabilityScope: ObservabilityScope ) throws -> [(PackageReference, Workspace.PackageStateChange)]? { let start = DispatchTime.now() @@ -200,7 +200,7 @@ extension Workspace { func _resolve( root: PackageGraphRootInput, explicitProduct: String?, - availableLibraries: [LibraryMetadata], + availableLibraries: [ProvidedLibrary], resolvedFileStrategy: ResolvedFileStrategy, observabilityScope: ObservabilityScope ) throws -> DependencyManifests { @@ -304,7 +304,7 @@ extension Workspace { func _resolveBasedOnResolvedVersionsFile( root: PackageGraphRootInput, explicitProduct: String?, - availableLibraries: [LibraryMetadata], + availableLibraries: [ProvidedLibrary], observabilityScope: ObservabilityScope ) throws -> DependencyManifests { let (manifests, precomputationResult) = try self.tryResolveBasedOnResolvedVersionsFile( @@ -343,7 +343,7 @@ extension Workspace { fileprivate func tryResolveBasedOnResolvedVersionsFile( root: PackageGraphRootInput, explicitProduct: String?, - availableLibraries: [LibraryMetadata], + availableLibraries: [ProvidedLibrary], observabilityScope: ObservabilityScope ) throws -> (DependencyManifests, ResolutionPrecomputationResult) { // Ensure the cache path exists. @@ -495,7 +495,7 @@ extension Workspace { func resolveAndUpdateResolvedFile( root: PackageGraphRootInput, explicitProduct: String? = nil, - availableLibraries: [LibraryMetadata], + availableLibraries: [ProvidedLibrary], forceResolution: Bool, constraints: [PackageContainerConstraint], observabilityScope: ObservabilityScope @@ -849,7 +849,7 @@ extension Workspace { dependencyManifests: DependencyManifests, pinsStore: PinsStore, constraints: [PackageContainerConstraint], - availableLibraries: [LibraryMetadata], + availableLibraries: [ProvidedLibrary], observabilityScope: ObservabilityScope ) throws -> ResolutionPrecomputationResult { let computedConstraints = @@ -1144,7 +1144,7 @@ extension Workspace { /// Creates resolver for the workspace. fileprivate func createResolver( pins: PinsStore.Pins, - availableLibraries: [LibraryMetadata], + availableLibraries: [ProvidedLibrary], observabilityScope: ObservabilityScope ) throws -> PubGrubDependencyResolver { var delegate: DependencyResolverDelegate diff --git a/Sources/Workspace/Workspace+Editing.swift b/Sources/Workspace/Workspace+Editing.swift index 86f7d085586..03939149bed 100644 --- a/Sources/Workspace/Workspace+Editing.swift +++ b/Sources/Workspace/Workspace+Editing.swift @@ -15,7 +15,7 @@ import class Basics.ObservabilityScope import struct Basics.RelativePath import func Basics.temp_await import struct PackageGraph.PackageGraphRootInput -import struct PackageModel.LibraryMetadata +import struct PackageModel.ProvidedLibrary import struct SourceControl.Revision import class TSCBasic.InMemoryFileSystem @@ -173,7 +173,7 @@ extension Workspace { dependency: ManagedDependency, forceRemove: Bool, root: PackageGraphRootInput? = nil, - availableLibraries: [LibraryMetadata], + availableLibraries: [ProvidedLibrary], observabilityScope: ObservabilityScope ) throws { // Compute if we need to force remove. diff --git a/Sources/Workspace/Workspace+Manifests.swift b/Sources/Workspace/Workspace+Manifests.swift index 48a229a9c85..e388d5d9feb 100644 --- a/Sources/Workspace/Workspace+Manifests.swift +++ b/Sources/Workspace/Workspace+Manifests.swift @@ -28,7 +28,7 @@ import struct PackageGraph.PackageGraphRoot import class PackageLoading.ManifestLoader import struct PackageLoading.ManifestValidator import struct PackageLoading.ToolsVersionParser -import struct PackageModel.LibraryMetadata +import struct PackageModel.ProvidedLibrary import class PackageModel.Manifest import struct PackageModel.PackageIdentity import struct PackageModel.PackageReference @@ -61,7 +61,7 @@ extension Workspace { private let workspace: Workspace - private let availableLibraries: [LibraryMetadata] + private let availableLibraries: [ProvidedLibrary] private let observabilityScope: ObservabilityScope @@ -81,7 +81,7 @@ extension Workspace { fileSystem: FileSystem )], workspace: Workspace, - availableLibraries: [LibraryMetadata], + availableLibraries: [ProvidedLibrary], observabilityScope: ObservabilityScope ) { self.root = root @@ -173,7 +173,7 @@ extension Workspace { fileSystem: FileSystem )], workspace: Workspace, - availableLibraries: [LibraryMetadata], + availableLibraries: [ProvidedLibrary], observabilityScope: ObservabilityScope ) throws -> ( @@ -205,7 +205,7 @@ extension Workspace { }) let identitiesAvailableInSDK = availableLibraries.flatMap { - $0.identities.map { + $0.metadata.identities.map { $0.ref }.filter { // We "trust the process" here, if an identity from the SDK is available, filter it. @@ -438,7 +438,7 @@ extension Workspace { public func loadDependencyManifests( root: PackageGraphRoot, automaticallyAddManagedDependencies: Bool = false, - availableLibraries: [LibraryMetadata], + availableLibraries: [ProvidedLibrary], observabilityScope: ObservabilityScope ) throws -> DependencyManifests { let prepopulateManagedDependencies: ([PackageReference]) throws -> Void = { refs in @@ -821,7 +821,7 @@ extension Workspace { /// If some edited dependency is removed from the file system, mark it as unedited and /// fallback on the original checkout. private func fixManagedDependencies( - availableLibraries: [LibraryMetadata], + availableLibraries: [ProvidedLibrary], observabilityScope: ObservabilityScope ) { // Reset managed dependencies if the state file was removed during the lifetime of the Workspace object. diff --git a/Sources/Workspace/Workspace.swift b/Sources/Workspace/Workspace.swift index 5574916885c..4e81b3d7e81 100644 --- a/Sources/Workspace/Workspace.swift +++ b/Sources/Workspace/Workspace.swift @@ -572,7 +572,7 @@ public class Workspace { ) } - fileprivate var providedLibraries: [LibraryMetadata] { + fileprivate var providedLibraries: [ProvidedLibrary] { // Note: Eventually, we should get these from the individual SDKs, but the first step is providing the metadata centrally in the toolchain. return self.hostToolchain.providedLibraries } @@ -626,7 +626,7 @@ extension Workspace { packageName: String, forceRemove: Bool, root: PackageGraphRootInput, - availableLibraries: [LibraryMetadata], + availableLibraries: [ProvidedLibrary], observabilityScope: ObservabilityScope ) throws { guard let dependency = self.state.dependencies[.plain(packageName)] else { @@ -879,7 +879,7 @@ extension Workspace { forceResolvedVersions: Bool = false, customXCTestMinimumDeploymentTargets: [PackageModel.Platform: PlatformVersion]? = .none, testEntryPointPath: AbsolutePath? = nil, - availableLibraries: [LibraryMetadata], + availableLibraries: [ProvidedLibrary], expectedSigningEntities: [PackageIdentity: RegistryReleaseMetadata.SigningEntity] = [:], observabilityScope: ObservabilityScope ) throws -> ModulesGraph { diff --git a/Tests/BuildTests/BuildOperationTests.swift b/Tests/BuildTests/BuildOperationTests.swift index 21c40cafb5d..35e72c63462 100644 --- a/Tests/BuildTests/BuildOperationTests.swift +++ b/Tests/BuildTests/BuildOperationTests.swift @@ -52,12 +52,15 @@ final class BuildOperationTests: XCTestCase { buildOp.detectUnexpressedDependencies( availableLibraries: [ .init( - identities: [ - .sourceControl(url: .init("https://example.com/org/foo")) - ], - version: "1.0.0", - productName: "Best", - schemaVersion: 1 + location: "/foo", + metadata: .init( + identities: [ + .sourceControl(url: .init("https://example.com/org/foo")) + ], + version: "1.0.0", + productName: "Best", + schemaVersion: 1 + ) ) ], targetDependencyMap: ["Lunch": []] diff --git a/Tests/PackageGraphTests/PubgrubTests.swift b/Tests/PackageGraphTests/PubgrubTests.swift index 7684413fa48..782cb476664 100644 --- a/Tests/PackageGraphTests/PubgrubTests.swift +++ b/Tests/PackageGraphTests/PubgrubTests.swift @@ -2023,13 +2023,16 @@ final class PubGrubTestsBasicGraphs: XCTestCase { try builder.serve(fooRef, at: .version(.init(stringLiteral: "1.2.0"))) try builder.serve(fooRef, at: .version(.init(stringLiteral: "2.0.0"))) - let availableLibraries: [LibraryMetadata] = [ + let availableLibraries: [ProvidedLibrary] = [ .init( - identities: [.sourceControl(url: "https://example.com/org/foo")], - version: "1.0.0", - productName: nil, - schemaVersion: 1 - ), + location: .init("/foo"), + metadata: .init( + identities: [.sourceControl(url: "https://example.com/org/foo")], + version: "1.0.0", + productName: "foo", + schemaVersion: 1 + ) + ) ] let resolver = builder.create(availableLibraries: availableLibraries) @@ -2080,13 +2083,16 @@ final class PubGrubTestsBasicGraphs: XCTestCase { try builder.serve("target", at: "1.0.0") try builder.serve("target", at: "2.0.0") - let availableLibraries: [LibraryMetadata] = [ + let availableLibraries: [ProvidedLibrary] = [ .init( - identities: [.sourceControl(url: "https://example.com/org/foo")], - version: "1.1.0", - productName: nil, - schemaVersion: 1 - ), + location: .init("/foo"), + metadata: .init( + identities: [.sourceControl(url: "https://example.com/org/foo")], + version: "1.1.0", + productName: "foo", + schemaVersion: 1 + ) + ) ] let resolver = builder.create(availableLibraries: availableLibraries) @@ -2126,13 +2132,16 @@ final class PubGrubTestsBasicGraphs: XCTestCase { "bar": [fooRef: (.versionSet(.range(.upToNextMinor(from: "2.0.0"))), .everything)], ]) - let availableLibraries: [LibraryMetadata] = [ + let availableLibraries: [ProvidedLibrary] = [ .init( - identities: [.sourceControl(url: "https://example.com/org/foo")], - version: "1.0.0", - productName: nil, - schemaVersion: 1 - ), + location: .init("/foo"), + metadata: .init( + identities: [.sourceControl(url: "https://example.com/org/foo")], + version: "1.0.0", + productName: "foo", + schemaVersion: 1 + ) + ) ] let resolver = builder.create(availableLibraries: availableLibraries) @@ -3704,7 +3713,7 @@ class DependencyGraphBuilder { func create( pins: PinsStore.Pins = [:], - availableLibraries: [LibraryMetadata] = [], + availableLibraries: [ProvidedLibrary] = [], delegate: DependencyResolverDelegate? = .none ) -> PubGrubDependencyResolver { defer { From 139c6fad31c9ad13bb3dde9053e2f7b3e31c9414 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 18 Apr 2024 11:45:39 -0700 Subject: [PATCH 111/159] [Resolution/Loading] Handle packages backed by a provided library Instead of eliding provided library packages, start returning them from resolution as a special `.providedLibrary` reference and augment manifest loader to build manifests for such libraries based on contents of the library's directory (each `.swiftmodule` becomes a `.providedLibrary` target). --- .../PubGrub/PubGrubDependencyResolver.swift | 18 +++--- .../ManifestLoader+Validation.swift | 5 ++ Sources/PackageLoading/ManifestLoader.swift | 60 +++++++++++++++++++ 3 files changed, 75 insertions(+), 8 deletions(-) diff --git a/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift b/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift index d6f5ddf3983..00eead9a16f 100644 --- a/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift +++ b/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift @@ -231,7 +231,7 @@ public struct PubGrubDependencyResolver { continue } - let package = assignment.term.node.package + var package = assignment.term.node.package let boundVersion: BoundVersion switch assignment.term.requirement { @@ -241,16 +241,18 @@ public struct PubGrubDependencyResolver { throw InternalError("unexpected requirement value for assignment \(assignment.term)") } - // Strip packages that have prebuilt libraries only if they match library version. - // - // FIXME: This is built on assumption that libraries are part of the SDK and are - // always available in include/library paths, but what happens if they are - // part of a toolchain instead? Builder needs an indicator that certain path - // has to be included when building packages that depend on prebuilt libraries. if let library = package.matchingPrebuiltLibrary(in: availableLibraries), boundVersion == .version(.init(stringLiteral: library.version)) { - continue + guard case .remoteSourceControl(let url) = package.kind else { + throw InternalError("Matched provided library against invalid package: \(package)") + } + + package = .providedLibrary( + identity: package.identity, + origin: url, + path: library.location + ) } let products = assignment.term.node.productFilter diff --git a/Sources/PackageLoading/ManifestLoader+Validation.swift b/Sources/PackageLoading/ManifestLoader+Validation.swift index af215dd5298..768286b05ef 100644 --- a/Sources/PackageLoading/ManifestLoader+Validation.swift +++ b/Sources/PackageLoading/ManifestLoader+Validation.swift @@ -34,6 +34,11 @@ public struct ManifestValidator { /// Validate the provided manifest. public func validate() -> [Basics.Diagnostic] { + // Provided library manifest is synthesized by the package manager. + if case .providedLibrary = self.manifest.packageKind { + return [] + } + var diagnostics = [Basics.Diagnostic]() diagnostics += self.validateTargets() diff --git a/Sources/PackageLoading/ManifestLoader.swift b/Sources/PackageLoading/ManifestLoader.swift index 9424af7e617..bf8f55fda45 100644 --- a/Sources/PackageLoading/ManifestLoader.swift +++ b/Sources/PackageLoading/ManifestLoader.swift @@ -206,6 +206,19 @@ extension ManifestLoaderProtocol { completion: @escaping (Result) -> Void ) { do { + if case .providedLibrary = packageKind { + let manifest = try self.loadLibrary( + fileSystem: fileSystem, + packagePath: packagePath, + packageKind: packageKind, + packageIdentity: packageIdentity, + packageLocation: packageLocation, + packageVersion: packageVersion?.version + ) + completion(.success(manifest)) + return + } + // find the manifest path and parse it's tools-version let manifestPath = try ManifestLoader.findManifest(packagePath: packagePath, fileSystem: fileSystem, currentToolsVersion: currentToolsVersion) let manifestToolsVersion = try ToolsVersionParser.parse(manifestPath: manifestPath, fileSystem: fileSystem) @@ -266,6 +279,53 @@ extension ManifestLoaderProtocol { ) } } + + private func loadLibrary( + fileSystem: FileSystem, + packagePath: AbsolutePath, + packageKind: PackageReference.Kind, + packageIdentity: PackageIdentity, + packageLocation: String, + packageVersion: Version? + ) throws -> Manifest { + let names = try fileSystem.getDirectoryContents(packagePath).filter { + $0.hasSuffix("swiftmodule") + }.map { + let components = $0.split(separator: ".") + return String(components[0]) + } + + let products: [ProductDescription] = try names.map { + try .init(name: $0, type: .library(.automatic), targets: [$0]) + } + + let targets: [TargetDescription] = try names.map { + try .init( + name: $0, + path: packagePath.pathString, + type: .providedLibrary + ) + } + + return .init( + displayName: packageIdentity.description, + path: packagePath.appending(component: "provided-libraries.json"), + packageKind: packageKind, + packageLocation: packageLocation, + defaultLocalization: nil, + platforms: [], + version: packageVersion, + revision: nil, + toolsVersion: .v6_0, + pkgConfig: nil, + providers: nil, + cLanguageStandard: nil, + cxxLanguageStandard: nil, + swiftLanguageVersions: nil, + products: products, + targets: targets + ) + } } // MARK: - ManifestLoader From 6d7e7953845dfb8e251ed4d143f014fbeffe539c Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 19 Apr 2024 13:04:37 -0700 Subject: [PATCH 112/159] [PubGrub] Expand provided library resolution tests Since provided libraries are now returned from resultion we need to double-check if resultion returns proper package reference kinds for packages with provided libraries. --- Sources/PackageGraph/CMakeLists.txt | 1 + .../ProvidedLibraryPackageContainer.swift | 3 +- .../PubGrub/ContainerProvider.swift | 8 ++ Sources/Workspace/CMakeLists.txt | 1 - .../Workspace/Workspace+Dependencies.swift | 1 + .../Workspace+PackageContainer.swift | 1 + Tests/PackageGraphTests/PubgrubTests.swift | 91 ++++++++++++++++--- 7 files changed, 92 insertions(+), 14 deletions(-) rename Sources/{Workspace/PackageContainer => PackageGraph}/ProvidedLibraryPackageContainer.swift (94%) diff --git a/Sources/PackageGraph/CMakeLists.txt b/Sources/PackageGraph/CMakeLists.txt index b95b1eb9feb..13d010b11a7 100644 --- a/Sources/PackageGraph/CMakeLists.txt +++ b/Sources/PackageGraph/CMakeLists.txt @@ -20,6 +20,7 @@ add_library(PackageGraph PackageModel+Extensions.swift PackageRequirement.swift PinsStore.swift + ProvidedLibraryPackageContainer.swift Resolution/PubGrub/Assignment.swift Resolution/PubGrub/ContainerProvider.swift Resolution/PubGrub/DiagnosticReportBuilder.swift diff --git a/Sources/Workspace/PackageContainer/ProvidedLibraryPackageContainer.swift b/Sources/PackageGraph/ProvidedLibraryPackageContainer.swift similarity index 94% rename from Sources/Workspace/PackageContainer/ProvidedLibraryPackageContainer.swift rename to Sources/PackageGraph/ProvidedLibraryPackageContainer.swift index f423dcf454a..8f95ca0f7ad 100644 --- a/Sources/Workspace/PackageContainer/ProvidedLibraryPackageContainer.swift +++ b/Sources/PackageGraph/ProvidedLibraryPackageContainer.swift @@ -12,12 +12,13 @@ import Basics import Dispatch -import PackageGraph import PackageLoading import PackageModel import struct TSCUtility.Version +/// TODO: This could be removed once logic to handle provided libraries is integrated +/// into a \c PubGrubPackageContainer. public struct ProvidedLibraryPackageContainer: PackageContainer { public let package: PackageReference diff --git a/Sources/PackageGraph/Resolution/PubGrub/ContainerProvider.swift b/Sources/PackageGraph/Resolution/PubGrub/ContainerProvider.swift index 46cd49a9fbf..95e43367f6e 100644 --- a/Sources/PackageGraph/Resolution/PubGrub/ContainerProvider.swift +++ b/Sources/PackageGraph/Resolution/PubGrub/ContainerProvider.swift @@ -97,6 +97,14 @@ final class ContainerProvider { ) { result in let result = result.tryMap { container -> PubGrubPackageContainer in let pubGrubContainer = PubGrubPackageContainer(underlying: container, pins: self.pins) + + // This container is not cached because it's intended to be transparent + // and requested only when forming final assignments. Caching it would + // mean that subsequent calls to `solve` would pick it up. + if case .providedLibrary = package.kind { + return pubGrubContainer + } + // only cache positive results self.containersCache[package] = pubGrubContainer return pubGrubContainer diff --git a/Sources/Workspace/CMakeLists.txt b/Sources/Workspace/CMakeLists.txt index 447c3353684..7817b6b4a43 100644 --- a/Sources/Workspace/CMakeLists.txt +++ b/Sources/Workspace/CMakeLists.txt @@ -15,7 +15,6 @@ add_library(Workspace ManagedArtifact.swift ManagedDependency.swift PackageContainer/FileSystemPackageContainer.swift - PackageContainer/ProvidedLibraryPackageContainer.swift PackageContainer/RegistryPackageContainer.swift PackageContainer/SourceControlPackageContainer.swift ResolvedFileWatcher.swift diff --git a/Sources/Workspace/Workspace+Dependencies.swift b/Sources/Workspace/Workspace+Dependencies.swift index 3f6a917cf07..6b9bd9897a3 100644 --- a/Sources/Workspace/Workspace+Dependencies.swift +++ b/Sources/Workspace/Workspace+Dependencies.swift @@ -33,6 +33,7 @@ import struct PackageGraph.ObservabilityDependencyResolverDelegate import struct PackageGraph.PackageContainerConstraint import struct PackageGraph.PackageGraphRoot import struct PackageGraph.PackageGraphRootInput +import struct PackageGraph.ProvidedLibraryPackageContainer import class PackageGraph.PinsStore import struct PackageGraph.PubGrubDependencyResolver import struct PackageGraph.Term diff --git a/Sources/Workspace/Workspace+PackageContainer.swift b/Sources/Workspace/Workspace+PackageContainer.swift index 4ba1016756a..ca5e857b343 100644 --- a/Sources/Workspace/Workspace+PackageContainer.swift +++ b/Sources/Workspace/Workspace+PackageContainer.swift @@ -17,6 +17,7 @@ import enum PackageFingerprint.FingerprintCheckingMode import enum PackageGraph.ContainerUpdateStrategy import protocol PackageGraph.PackageContainer import protocol PackageGraph.PackageContainerProvider +import struct PackageGraph.ProvidedLibraryPackageContainer import struct PackageModel.PackageReference // MARK: - Package container provider diff --git a/Tests/PackageGraphTests/PubgrubTests.swift b/Tests/PackageGraphTests/PubgrubTests.swift index 782cb476664..8920d14211e 100644 --- a/Tests/PackageGraphTests/PubgrubTests.swift +++ b/Tests/PackageGraphTests/PubgrubTests.swift @@ -2044,12 +2044,18 @@ final class PubGrubTestsBasicGraphs: XCTestCase { ]) let result = resolver.solve(constraints: dependencies1) - // Available libraries are filtered from the resolver results, so this is expected to be empty. - AssertResult(result, []) + print(try result.get()) + AssertResult(result, [ + ( + "foo", + .providedLibrary(.init("https://example.com/org/foo"), .init("/foo")), + .version(.init(stringLiteral: "1.0.0")) + ), + ]) let result2 = resolver.solve(constraints: dependencies2) AssertResult(result2, [ - ("foo", .version(.init(stringLiteral: "1.2.0"))), + ("foo", fooRef.kind, .version(.init(stringLiteral: "1.2.0"))), ]) } @@ -2101,14 +2107,23 @@ final class PubGrubTestsBasicGraphs: XCTestCase { "target": (.versionSet(.range(.upToNextMajor(from: "2.0.0"))), .everything), ]) - // This behavior requires an explanation - "foo" is elided because 1.1.0 is prebuilt. - // It matches "root" requirements but without prebuilt library the solver would pick - // "1.0.0" because "foo" 1.1.0 dependency version requirements are incompatible with - // "target" 2.0.0. + // This behavior requires an explanation - "foo" is selected to be 1.1.0 because its + // prebuilt matches "root" requirements but without prebuilt library the solver would + // pick "1.0.0" because "foo" 1.1.0 dependency version requirements are incompatible + // with "target" 2.0.0. let result = resolver.solve(constraints: dependencies) AssertResult(result, [ - ("target", .version(.init(stringLiteral: "2.0.0"))), + ( + "foo", + .providedLibrary(.init("https://example.com/org/foo"), .init("/foo")), + .version(.init(stringLiteral: "1.1.0")) + ), + ( + "target", + .localSourceControl("/target"), + .version(.init(stringLiteral: "2.0.0")) + ), ]) } @@ -2152,8 +2167,8 @@ final class PubGrubTestsBasicGraphs: XCTestCase { let result = resolver.solve(constraints: dependencies) AssertResult(result, [ - ("foo", .version(.init(stringLiteral: "1.1.0"))), - ("bar", .version(.init(stringLiteral: "1.0.0"))), + ("foo", fooRef.kind, .version(.init(stringLiteral: "1.1.0"))), + ("bar", .localSourceControl("/bar"), .version(.init(stringLiteral: "1.0.0"))), ]) } } @@ -3297,6 +3312,22 @@ private func AssertBindings( _ packages: [(identity: PackageIdentity, version: BoundVersion)], file: StaticString = #file, line: UInt = #line +) { + AssertBindings( + bindings, + packages.map { + (identity: $0, kind: nil, version: $1) + }, + file: file, + line: line + ) +} + +private func AssertBindings( + _ bindings: [DependencyResolverBinding], + _ packages: [(identity: PackageIdentity, kind: PackageReference.Kind?, version: BoundVersion)], + file: StaticString = #file, + line: UInt = #line ) { if bindings.count > packages.count { let unexpectedBindings = bindings @@ -3314,7 +3345,17 @@ private func AssertBindings( ) } for package in packages { - guard let binding = bindings.first(where: { $0.package.identity == package.identity }) else { + guard let binding = bindings.first(where: { + if $0.package.identity != package.identity { + return false + } + + if let kind = package.kind, $0.package.kind != kind { + return false + } + + return true + }) else { XCTFail("No binding found for \(package.identity).", file: file, line: line) continue } @@ -3335,10 +3376,24 @@ private func AssertResult( _ packages: [(identifier: String, version: BoundVersion)], file: StaticString = #file, line: UInt = #line +) { + AssertResult(result, packages.map { ($0, nil, $1) }, file: file, line: line) +} + +private func AssertResult( + _ result: Result<[DependencyResolverBinding], Error>, + _ packages: [(identifier: String, kind: PackageReference.Kind?, version: BoundVersion)], + file: StaticString = #file, + line: UInt = #line ) { switch result { case .success(let bindings): - AssertBindings(bindings, packages.map { (PackageIdentity($0.identifier), $0.version) }, file: file, line: line) + AssertBindings( + bindings, + packages.map { (PackageIdentity($0.identifier), $0.kind, $0.version) }, + file: file, + line: line + ) case .failure(let error): XCTFail("Unexpected error: \(error)", file: file, line: line) } @@ -3544,6 +3599,18 @@ public struct MockProvider: PackageContainerProvider { ) -> Void ) { queue.async { + if case .providedLibrary(_, _) = package.kind { + do { + let container = try ProvidedLibraryPackageContainer( + package: package, + observabilityScope: observabilityScope + ) + return completion(.success(container)) + } catch { + return completion(.failure(error)) + } + } + completion( self.containersByIdentifier[package].map { .success($0) } ?? .failure(_MockLoadingError.unknownModule) From 78c68fe73cdf43b13ffd51cec73ccb5940e5110f Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 22 Apr 2024 11:17:43 -0700 Subject: [PATCH 113/159] Remove all extraneous uses of provided libraries throughout code base Only workspace and PubGrub dependency resolver need to know about provided libraries. Post-resolution everything acts based on managed dependencies and package identifiers. --- .../PackageCommands/EditCommands.swift | 1 - Sources/CoreCommands/SwiftCommandState.swift | 1 - Sources/PackageGraph/ModulesGraph.swift | 1 - .../ProvidedLibraryPackageContainer.swift | 4 +-- .../PubGrub/ContainerProvider.swift | 7 ++--- .../PubGrub/PubGrubDependencyResolver.swift | 7 +++-- Sources/SPMTestSupport/MockWorkspace.swift | 6 ---- .../Workspace/Workspace+Dependencies.swift | 25 ++-------------- Sources/Workspace/Workspace+Editing.swift | 2 -- Sources/Workspace/Workspace+Manifests.swift | 29 ------------------- Sources/Workspace/Workspace.swift | 13 ++------- Tests/CommandsTests/PackageCommandTests.swift | 1 - Tests/FunctionalTests/PluginTests.swift | 4 --- .../PluginInvocationTests.swift | 6 ---- 14 files changed, 12 insertions(+), 95 deletions(-) diff --git a/Sources/Commands/PackageCommands/EditCommands.swift b/Sources/Commands/PackageCommands/EditCommands.swift index db6a4e8004c..f20d5c44a2f 100644 --- a/Sources/Commands/PackageCommands/EditCommands.swift +++ b/Sources/Commands/PackageCommands/EditCommands.swift @@ -74,7 +74,6 @@ extension SwiftPackageCommand { packageName: packageName, forceRemove: shouldForceRemove, root: swiftCommandState.getWorkspaceRoot(), - availableLibraries: swiftCommandState.getHostToolchain().providedLibraries, observabilityScope: swiftCommandState.observabilityScope ) } diff --git a/Sources/CoreCommands/SwiftCommandState.swift b/Sources/CoreCommands/SwiftCommandState.swift index 754a5401ea4..b94ff2268cd 100644 --- a/Sources/CoreCommands/SwiftCommandState.swift +++ b/Sources/CoreCommands/SwiftCommandState.swift @@ -607,7 +607,6 @@ package final class SwiftCommandState { explicitProduct: explicitProduct, forceResolvedVersions: options.resolver.forceResolvedVersions, testEntryPointPath: testEntryPointPath, - availableLibraries: self.getHostToolchain().providedLibraries, observabilityScope: self.observabilityScope ) diff --git a/Sources/PackageGraph/ModulesGraph.swift b/Sources/PackageGraph/ModulesGraph.swift index 49b2ea1f979..345724c2971 100644 --- a/Sources/PackageGraph/ModulesGraph.swift +++ b/Sources/PackageGraph/ModulesGraph.swift @@ -490,7 +490,6 @@ public func loadModulesGraph( shouldCreateMultipleTestProducts: shouldCreateMultipleTestProducts, createREPLProduct: createREPLProduct, customXCTestMinimumDeploymentTargets: customXCTestMinimumDeploymentTargets, - availableLibraries: [], fileSystem: fileSystem, observabilityScope: observabilityScope ) diff --git a/Sources/PackageGraph/ProvidedLibraryPackageContainer.swift b/Sources/PackageGraph/ProvidedLibraryPackageContainer.swift index 8f95ca0f7ad..73c3bf6252a 100644 --- a/Sources/PackageGraph/ProvidedLibraryPackageContainer.swift +++ b/Sources/PackageGraph/ProvidedLibraryPackageContainer.swift @@ -46,7 +46,7 @@ public struct ProvidedLibraryPackageContainer: PackageContainer { } public func toolsVersion(for version: Version) throws -> ToolsVersion { - .v6_0 + .v4 } public func toolsVersionsAppropriateVersionsDescending() throws -> [Version] { @@ -54,7 +54,7 @@ public struct ProvidedLibraryPackageContainer: PackageContainer { } public func versionsAscending() throws -> [Version] { - [] // TODO + [] } public func getDependencies(at version: Version, productFilter: ProductFilter) throws -> [PackageContainerConstraint] { diff --git a/Sources/PackageGraph/Resolution/PubGrub/ContainerProvider.swift b/Sources/PackageGraph/Resolution/PubGrub/ContainerProvider.swift index 95e43367f6e..dacf263937d 100644 --- a/Sources/PackageGraph/Resolution/PubGrub/ContainerProvider.swift +++ b/Sources/PackageGraph/Resolution/PubGrub/ContainerProvider.swift @@ -115,12 +115,9 @@ final class ContainerProvider { } /// Starts prefetching the given containers. - func prefetch(containers identifiers: [PackageReference], availableLibraries: [ProvidedLibrary]) { - let filteredIdentifiers = identifiers.filter { - $0.matchingPrebuiltLibrary(in: availableLibraries) == nil - } + func prefetch(containers identifiers: [PackageReference]) { // Process each container. - for identifier in filteredIdentifiers { + for identifier in identifiers { var needsFetching = false self.prefetches.memoize(identifier) { let group = DispatchGroup() diff --git a/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift b/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift index 00eead9a16f..2aaecf83cba 100644 --- a/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift +++ b/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift @@ -203,7 +203,7 @@ public struct PubGrubDependencyResolver { let pins = self.pins.values .map(\.packageRef) .filter { !inputs.overriddenPackages.keys.contains($0) } - self.provider.prefetch(containers: pins, availableLibraries: self.availableLibraries) + self.provider.prefetch(containers: pins) } let state = State(root: root, overriddenPackages: inputs.overriddenPackages) @@ -500,8 +500,9 @@ public struct PubGrubDependencyResolver { // initiate prefetch of known packages that will be used to make the decision on the next step self.provider.prefetch( - containers: state.solution.undecided.map(\.node.package), - availableLibraries: self.availableLibraries + containers: state.solution.undecided.map(\.node.package).filter { + $0.matchingPrebuiltLibrary(in: self.availableLibraries) == nil + } ) // If decision making determines that no more decisions are to be diff --git a/Sources/SPMTestSupport/MockWorkspace.swift b/Sources/SPMTestSupport/MockWorkspace.swift index 81056281f22..a09adf4079d 100644 --- a/Sources/SPMTestSupport/MockWorkspace.swift +++ b/Sources/SPMTestSupport/MockWorkspace.swift @@ -368,7 +368,6 @@ package final class MockWorkspace { packageName: packageName, forceRemove: forceRemove, root: rootInput, - availableLibraries: [], // assume no provided libraries for testing. observabilityScope: observability.topScope ) } @@ -467,7 +466,6 @@ package final class MockWorkspace { let graph = try workspace.loadPackageGraph( rootInput: rootInput, forceResolvedVersions: forceResolvedVersions, - availableLibraries: [], // assume no provided libraries for testing. expectedSigningEntities: expectedSigningEntities, observabilityScope: observability.topScope ) @@ -506,7 +504,6 @@ package final class MockWorkspace { try workspace.loadPackageGraph( rootInput: rootInput, forceResolvedVersions: forceResolvedVersions, - availableLibraries: [], // assume no provided libraries for testing. observabilityScope: observability.topScope ) } @@ -532,7 +529,6 @@ package final class MockWorkspace { let dependencyManifests = try workspace.loadDependencyManifests( root: root, - availableLibraries: [], // assume no provided libraries for testing. observabilityScope: observability.topScope ) @@ -541,7 +537,6 @@ package final class MockWorkspace { dependencyManifests: dependencyManifests, pinsStore: pinsStore, constraints: [], - availableLibraries: [], // assume no provided libraries for testing. observabilityScope: observability.topScope ) @@ -752,7 +747,6 @@ package final class MockWorkspace { let graphRoot = PackageGraphRoot(input: rootInput, manifests: rootManifests, observabilityScope: observability.topScope) let manifests = try workspace.loadDependencyManifests( root: graphRoot, - availableLibraries: [], // assume no provided libraries for testing. observabilityScope: observability.topScope ) result(manifests, observability.diagnostics) diff --git a/Sources/Workspace/Workspace+Dependencies.swift b/Sources/Workspace/Workspace+Dependencies.swift index 6b9bd9897a3..0fab90aa3f1 100644 --- a/Sources/Workspace/Workspace+Dependencies.swift +++ b/Sources/Workspace/Workspace+Dependencies.swift @@ -58,7 +58,6 @@ extension Workspace { root: PackageGraphRootInput, packages: [String] = [], dryRun: Bool = false, - availableLibraries: [ProvidedLibrary], observabilityScope: ObservabilityScope ) throws -> [(PackageReference, Workspace.PackageStateChange)]? { let start = DispatchTime.now() @@ -89,7 +88,6 @@ extension Workspace { ) let currentManifests = try self.loadDependencyManifests( root: graphRoot, - availableLibraries: availableLibraries, observabilityScope: observabilityScope ) @@ -124,7 +122,6 @@ extension Workspace { // Resolve the dependencies. let resolver = try self.createResolver( pins: pins, - availableLibraries: availableLibraries, observabilityScope: observabilityScope ) self.activeResolver = resolver @@ -167,7 +164,6 @@ extension Workspace { // Load the updated manifests. let updatedDependencyManifests = try self.loadDependencyManifests( root: graphRoot, - availableLibraries: availableLibraries, observabilityScope: observabilityScope ) // If we have missing packages, something is fundamentally wrong with the resolution of the graph @@ -201,7 +197,6 @@ extension Workspace { func _resolve( root: PackageGraphRootInput, explicitProduct: String?, - availableLibraries: [ProvidedLibrary], resolvedFileStrategy: ResolvedFileStrategy, observabilityScope: ObservabilityScope ) throws -> DependencyManifests { @@ -217,7 +212,6 @@ extension Workspace { return try self._resolveBasedOnResolvedVersionsFile( root: root, explicitProduct: explicitProduct, - availableLibraries: availableLibraries, observabilityScope: observabilityScope ) case .update(let forceResolution): @@ -254,7 +248,6 @@ extension Workspace { let (manifests, precomputationResult) = try self.tryResolveBasedOnResolvedVersionsFile( root: root, explicitProduct: explicitProduct, - availableLibraries: availableLibraries, observabilityScope: observabilityScope ) switch precomputationResult { @@ -278,7 +271,6 @@ extension Workspace { return try self.resolveAndUpdateResolvedFile( root: root, explicitProduct: explicitProduct, - availableLibraries: availableLibraries, forceResolution: forceResolution, constraints: [], observabilityScope: observabilityScope @@ -305,13 +297,11 @@ extension Workspace { func _resolveBasedOnResolvedVersionsFile( root: PackageGraphRootInput, explicitProduct: String?, - availableLibraries: [ProvidedLibrary], observabilityScope: ObservabilityScope ) throws -> DependencyManifests { let (manifests, precomputationResult) = try self.tryResolveBasedOnResolvedVersionsFile( root: root, explicitProduct: explicitProduct, - availableLibraries: availableLibraries, observabilityScope: observabilityScope ) switch precomputationResult { @@ -344,7 +334,6 @@ extension Workspace { fileprivate func tryResolveBasedOnResolvedVersionsFile( root: PackageGraphRootInput, explicitProduct: String?, - availableLibraries: [ProvidedLibrary], observabilityScope: ObservabilityScope ) throws -> (DependencyManifests, ResolutionPrecomputationResult) { // Ensure the cache path exists. @@ -371,7 +360,6 @@ extension Workspace { return try ( self.loadDependencyManifests( root: graphRoot, - availableLibraries: availableLibraries, observabilityScope: observabilityScope ), .notRequired @@ -464,7 +452,6 @@ extension Workspace { let currentManifests = try self.loadDependencyManifests( root: graphRoot, automaticallyAddManagedDependencies: true, - availableLibraries: availableLibraries, observabilityScope: observabilityScope ) @@ -479,7 +466,6 @@ extension Workspace { dependencyManifests: currentManifests, pinsStore: pinsStore, constraints: [], - availableLibraries: availableLibraries, observabilityScope: observabilityScope ) @@ -496,7 +482,6 @@ extension Workspace { func resolveAndUpdateResolvedFile( root: PackageGraphRootInput, explicitProduct: String? = nil, - availableLibraries: [ProvidedLibrary], forceResolution: Bool, constraints: [PackageContainerConstraint], observabilityScope: ObservabilityScope @@ -524,7 +509,6 @@ extension Workspace { ) let currentManifests = try self.loadDependencyManifests( root: graphRoot, - availableLibraries: availableLibraries, observabilityScope: observabilityScope ) guard !observabilityScope.errorsReported else { @@ -561,7 +545,6 @@ extension Workspace { dependencyManifests: currentManifests, pinsStore: pinsStore, constraints: constraints, - availableLibraries: availableLibraries, observabilityScope: observabilityScope ) @@ -597,7 +580,6 @@ extension Workspace { // Perform dependency resolution. let resolver = try self.createResolver( pins: pinsStore.pins, - availableLibraries: availableLibraries, observabilityScope: observabilityScope ) self.activeResolver = resolver @@ -628,7 +610,6 @@ extension Workspace { // Update the pinsStore. let updatedDependencyManifests = try self.loadDependencyManifests( root: graphRoot, - availableLibraries: availableLibraries, observabilityScope: observabilityScope ) // If we still have missing packages, something is fundamentally wrong with the resolution of the graph @@ -850,7 +831,6 @@ extension Workspace { dependencyManifests: DependencyManifests, pinsStore: PinsStore, constraints: [PackageContainerConstraint], - availableLibraries: [ProvidedLibrary], observabilityScope: ObservabilityScope ) throws -> ResolutionPrecomputationResult { let computedConstraints = @@ -867,7 +847,7 @@ extension Workspace { let resolver = PubGrubDependencyResolver( provider: precomputationProvider, pins: pinsStore.pins, - availableLibraries: availableLibraries, + availableLibraries: self.providedLibraries, observabilityScope: observabilityScope ) let result = resolver.solve(constraints: computedConstraints) @@ -1145,7 +1125,6 @@ extension Workspace { /// Creates resolver for the workspace. fileprivate func createResolver( pins: PinsStore.Pins, - availableLibraries: [ProvidedLibrary], observabilityScope: ObservabilityScope ) throws -> PubGrubDependencyResolver { var delegate: DependencyResolverDelegate @@ -1162,7 +1141,7 @@ extension Workspace { return PubGrubDependencyResolver( provider: packageContainerProvider, pins: pins, - availableLibraries: availableLibraries, + availableLibraries: self.providedLibraries, skipDependenciesUpdates: self.configuration.skipDependenciesUpdates, prefetchBasedOnResolvedFile: self.configuration.prefetchBasedOnResolvedFile, observabilityScope: observabilityScope, diff --git a/Sources/Workspace/Workspace+Editing.swift b/Sources/Workspace/Workspace+Editing.swift index 03939149bed..cf429ad2eaa 100644 --- a/Sources/Workspace/Workspace+Editing.swift +++ b/Sources/Workspace/Workspace+Editing.swift @@ -173,7 +173,6 @@ extension Workspace { dependency: ManagedDependency, forceRemove: Bool, root: PackageGraphRootInput? = nil, - availableLibraries: [ProvidedLibrary], observabilityScope: ObservabilityScope ) throws { // Compute if we need to force remove. @@ -238,7 +237,6 @@ extension Workspace { try self._resolve( root: root, explicitProduct: .none, - availableLibraries: availableLibraries, resolvedFileStrategy: .update(forceResolution: false), observabilityScope: observabilityScope ) diff --git a/Sources/Workspace/Workspace+Manifests.swift b/Sources/Workspace/Workspace+Manifests.swift index e388d5d9feb..3f6f36c051b 100644 --- a/Sources/Workspace/Workspace+Manifests.swift +++ b/Sources/Workspace/Workspace+Manifests.swift @@ -61,8 +61,6 @@ extension Workspace { private let workspace: Workspace - private let availableLibraries: [ProvidedLibrary] - private let observabilityScope: ObservabilityScope private let _dependencies: LoadableResult<( @@ -81,20 +79,17 @@ extension Workspace { fileSystem: FileSystem )], workspace: Workspace, - availableLibraries: [ProvidedLibrary], observabilityScope: ObservabilityScope ) { self.root = root self.dependencies = dependencies self.workspace = workspace - self.availableLibraries = availableLibraries self.observabilityScope = observabilityScope self._dependencies = LoadableResult { try Self.computeDependencies( root: root, dependencies: dependencies, workspace: workspace, - availableLibraries: availableLibraries, observabilityScope: observabilityScope ) } @@ -173,7 +168,6 @@ extension Workspace { fileSystem: FileSystem )], workspace: Workspace, - availableLibraries: [ProvidedLibrary], observabilityScope: ObservabilityScope ) throws -> ( @@ -204,17 +198,6 @@ extension Workspace { return PackageReference(identity: $0.key, kind: $0.1.packageKind) }) - let identitiesAvailableInSDK = availableLibraries.flatMap { - $0.metadata.identities.map { - $0.ref - }.filter { - // We "trust the process" here, if an identity from the SDK is available, filter it. - !availableIdentities.contains($0) - }.map { - $0.identity - } - } - var inputIdentities: OrderedCollections.OrderedSet = [] let inputNodes: [GraphLoadingNode] = root.packages.map { identity, package in inputIdentities.append(package.reference) @@ -292,11 +275,6 @@ extension Workspace { } requiredIdentities = inputIdentities.union(requiredIdentities) - let identitiesToFilter = requiredIdentities.filter { - return identitiesAvailableInSDK.contains($0.identity) - } - requiredIdentities = requiredIdentities.subtracting(identitiesToFilter) - // We should never have loaded a manifest we don't need. assert( availableIdentities.isSubset(of: requiredIdentities), @@ -438,7 +416,6 @@ extension Workspace { public func loadDependencyManifests( root: PackageGraphRoot, automaticallyAddManagedDependencies: Bool = false, - availableLibraries: [ProvidedLibrary], observabilityScope: ObservabilityScope ) throws -> DependencyManifests { let prepopulateManagedDependencies: ([PackageReference]) throws -> Void = { refs in @@ -481,7 +458,6 @@ extension Workspace { // Validates that all the managed dependencies are still present in the file system. self.fixManagedDependencies( - availableLibraries: availableLibraries, observabilityScope: observabilityScope ) guard !observabilityScope.errorsReported else { @@ -490,7 +466,6 @@ extension Workspace { root: root, dependencies: [], workspace: self, - availableLibraries: availableLibraries, observabilityScope: observabilityScope ) } @@ -564,7 +539,6 @@ extension Workspace { root: root, dependencies: [], workspace: self, - availableLibraries: availableLibraries, observabilityScope: observabilityScope ) } @@ -625,7 +599,6 @@ extension Workspace { root: root, dependencies: dependencies, workspace: self, - availableLibraries: availableLibraries, observabilityScope: observabilityScope ) } @@ -821,7 +794,6 @@ extension Workspace { /// If some edited dependency is removed from the file system, mark it as unedited and /// fallback on the original checkout. private func fixManagedDependencies( - availableLibraries: [ProvidedLibrary], observabilityScope: ObservabilityScope ) { // Reset managed dependencies if the state file was removed during the lifetime of the Workspace object. @@ -895,7 +867,6 @@ extension Workspace { try self.unedit( dependency: dependency, forceRemove: true, - availableLibraries: availableLibraries, observabilityScope: observabilityScope ) diff --git a/Sources/Workspace/Workspace.swift b/Sources/Workspace/Workspace.swift index 4e81b3d7e81..c27e9c5f523 100644 --- a/Sources/Workspace/Workspace.swift +++ b/Sources/Workspace/Workspace.swift @@ -572,9 +572,9 @@ public class Workspace { ) } - fileprivate var providedLibraries: [ProvidedLibrary] { + var providedLibraries: [ProvidedLibrary] { // Note: Eventually, we should get these from the individual SDKs, but the first step is providing the metadata centrally in the toolchain. - return self.hostToolchain.providedLibraries + self.hostToolchain.providedLibraries } } @@ -626,7 +626,6 @@ extension Workspace { packageName: String, forceRemove: Bool, root: PackageGraphRootInput, - availableLibraries: [ProvidedLibrary], observabilityScope: ObservabilityScope ) throws { guard let dependency = self.state.dependencies[.plain(packageName)] else { @@ -643,7 +642,6 @@ extension Workspace { dependency: dependency, forceRemove: forceRemove, root: root, - availableLibraries: availableLibraries, observabilityScope: observabilityScope ) } @@ -664,7 +662,6 @@ extension Workspace { try self._resolve( root: root, explicitProduct: explicitProduct, - availableLibraries: self.providedLibraries, resolvedFileStrategy: forceResolvedVersions ? .lockFile : forceResolution ? .update(forceResolution: true) : .bestEffort, observabilityScope: observabilityScope @@ -738,7 +735,6 @@ extension Workspace { // Run the resolution. try self.resolveAndUpdateResolvedFile( root: root, - availableLibraries: self.providedLibraries, forceResolution: false, constraints: [constraint], observabilityScope: observabilityScope @@ -756,7 +752,6 @@ extension Workspace { try self._resolveBasedOnResolvedVersionsFile( root: root, explicitProduct: .none, - availableLibraries: self.providedLibraries, observabilityScope: observabilityScope ) } @@ -867,7 +862,6 @@ extension Workspace { root: root, packages: packages, dryRun: dryRun, - availableLibraries: self.providedLibraries, observabilityScope: observabilityScope ) } @@ -879,7 +873,6 @@ extension Workspace { forceResolvedVersions: Bool = false, customXCTestMinimumDeploymentTargets: [PackageModel.Platform: PlatformVersion]? = .none, testEntryPointPath: AbsolutePath? = nil, - availableLibraries: [ProvidedLibrary], expectedSigningEntities: [PackageIdentity: RegistryReleaseMetadata.SigningEntity] = [:], observabilityScope: ObservabilityScope ) throws -> ModulesGraph { @@ -899,7 +892,6 @@ extension Workspace { let manifests = try self._resolve( root: root, explicitProduct: explicitProduct, - availableLibraries: availableLibraries, resolvedFileStrategy: forceResolvedVersions ? .lockFile : .bestEffort, observabilityScope: observabilityScope ) @@ -947,7 +939,6 @@ extension Workspace { try self.loadPackageGraph( rootInput: PackageGraphRootInput(packages: [rootPath]), explicitProduct: explicitProduct, - availableLibraries: self.providedLibraries, observabilityScope: observabilityScope ) } diff --git a/Tests/CommandsTests/PackageCommandTests.swift b/Tests/CommandsTests/PackageCommandTests.swift index 43a71bcf1a7..3dcad8f34d1 100644 --- a/Tests/CommandsTests/PackageCommandTests.swift +++ b/Tests/CommandsTests/PackageCommandTests.swift @@ -3328,7 +3328,6 @@ final class PackageCommandTests: CommandsTestCase { // Load the package graph. let _ = try workspace.loadPackageGraph( rootInput: rootInput, - availableLibraries: [], // assume no provided libraries for testing. observabilityScope: observability.topScope ) XCTAssertNoDiagnostics(observability.diagnostics) diff --git a/Tests/FunctionalTests/PluginTests.swift b/Tests/FunctionalTests/PluginTests.swift index 47218b62df3..c81842f822a 100644 --- a/Tests/FunctionalTests/PluginTests.swift +++ b/Tests/FunctionalTests/PluginTests.swift @@ -445,7 +445,6 @@ final class PluginTests: XCTestCase { // Load the package graph. let packageGraph = try workspace.loadPackageGraph( rootInput: rootInput, - availableLibraries: [], // assume no provided libraries for testing. observabilityScope: observability.topScope ) XCTAssertNoDiagnostics(observability.diagnostics) @@ -632,7 +631,6 @@ final class PluginTests: XCTestCase { // Load the package graph. let packageGraph = try workspace.loadPackageGraph( rootInput: rootInput, - availableLibraries: [], // assume no provided libraries for testing. observabilityScope: observability.topScope ) XCTAssertNoDiagnostics(observability.diagnostics) @@ -730,7 +728,6 @@ final class PluginTests: XCTestCase { // Load the package graph. let packageGraph = try workspace.loadPackageGraph( rootInput: rootInput, - availableLibraries: [], // assume no provided libraries for testing. observabilityScope: observability.topScope ) XCTAssertNoDiagnostics(observability.diagnostics) @@ -1046,7 +1043,6 @@ final class PluginTests: XCTestCase { // Load the package graph. let packageGraph = try workspace.loadPackageGraph( rootInput: rootInput, - availableLibraries: [], // assume no provided libraries for testing. observabilityScope: observability.topScope ) XCTAssert(packageGraph.packages.count == 4, "\(packageGraph.packages)") diff --git a/Tests/SPMBuildCoreTests/PluginInvocationTests.swift b/Tests/SPMBuildCoreTests/PluginInvocationTests.swift index 58668f9e5db..eb9be4cd970 100644 --- a/Tests/SPMBuildCoreTests/PluginInvocationTests.swift +++ b/Tests/SPMBuildCoreTests/PluginInvocationTests.swift @@ -306,7 +306,6 @@ final class PluginInvocationTests: XCTestCase { // Load the package graph. let packageGraph = try workspace.loadPackageGraph( rootInput: rootInput, - availableLibraries: [], // assume no provided libraries for testing. observabilityScope: observability.topScope ) XCTAssertNoDiagnostics(observability.diagnostics) @@ -687,7 +686,6 @@ final class PluginInvocationTests: XCTestCase { // Load the package graph. XCTAssertThrowsError(try workspace.loadPackageGraph( rootInput: rootInput, - availableLibraries: [], // assume no provided libraries for testing. observabilityScope: observability.topScope )) { error in var diagnosed = false @@ -767,7 +765,6 @@ final class PluginInvocationTests: XCTestCase { // Load the package graph. XCTAssertThrowsError(try workspace.loadPackageGraph( rootInput: rootInput, - availableLibraries: [], // assume no provided libraries for testing. observabilityScope: observability.topScope)) { error in var diagnosed = false if let realError = error as? PackageGraphError, @@ -877,7 +874,6 @@ final class PluginInvocationTests: XCTestCase { // Load the package graph. let packageGraph = try workspace.loadPackageGraph( rootInput: rootInput, - availableLibraries: [], // assume no provided libraries for testing. observabilityScope: observability.topScope ) XCTAssertNoDiagnostics(observability.diagnostics) @@ -1061,7 +1057,6 @@ final class PluginInvocationTests: XCTestCase { let graph = try workspace.loadPackageGraph( rootInput: rootInput, - availableLibraries: [], // assume no provided libraries for testing. observabilityScope: observability.topScope ) let dict = try await workspace.loadPluginImports(packageGraph: graph) @@ -1209,7 +1204,6 @@ final class PluginInvocationTests: XCTestCase { // Load the package graph. let packageGraph = try workspace.loadPackageGraph( rootInput: rootInput, - availableLibraries: [], // assume no provided libraries for testing. observabilityScope: observability.topScope ) XCTAssertNoDiagnostics(observability.diagnostics) From 04b26ea209ae8abb1fe32930dd1f965b12d21689 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 23 Apr 2024 11:18:38 -0700 Subject: [PATCH 114/159] Switch ProvidedLibrary.vesion to produce a `Version` --- .../Resolution/PubGrub/PubGrubDependencyResolver.swift | 10 ++++------ .../InstalledLibrariesSupport/LibraryMetadata.swift | 6 +++--- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift b/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift index 2aaecf83cba..c576bde35cf 100644 --- a/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift +++ b/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift @@ -242,7 +242,7 @@ public struct PubGrubDependencyResolver { } if let library = package.matchingPrebuiltLibrary(in: availableLibraries), - boundVersion == .version(.init(stringLiteral: library.version)) + boundVersion == .version(library.version) { guard case .remoteSourceControl(let url) = package.kind else { throw InternalError("Matched provided library against invalid package: \(package)") @@ -748,11 +748,9 @@ public struct PubGrubDependencyResolver { continue } - let version = Version(stringLiteral: library.version) - - if pkgTerm.requirement.contains(version) { - self.delegate?.didResolve(term: pkgTerm, version: version, duration: start.distance(to: .now())) - state.decide(pkgTerm.node, at: version) + if pkgTerm.requirement.contains(library.version) { + self.delegate?.didResolve(term: pkgTerm, version: library.version, duration: start.distance(to: .now())) + state.decide(pkgTerm.node, at: library.version) return completion(.success(pkgTerm.node)) } } diff --git a/Sources/PackageModel/InstalledLibrariesSupport/LibraryMetadata.swift b/Sources/PackageModel/InstalledLibrariesSupport/LibraryMetadata.swift index 362dae238cf..bde7b74ecfc 100644 --- a/Sources/PackageModel/InstalledLibrariesSupport/LibraryMetadata.swift +++ b/Sources/PackageModel/InstalledLibrariesSupport/LibraryMetadata.swift @@ -11,14 +11,14 @@ //===----------------------------------------------------------------------===// import Basics -import Foundation +import struct TSCUtility.Version public struct ProvidedLibrary { public let location: AbsolutePath public let metadata: LibraryMetadata - public var version: String { - metadata.version + public var version: Version { + .init(stringLiteral: metadata.version) } } From bb7f9e783d9f27e481d40904133270e93ca88c1f Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 24 Apr 2024 10:00:40 -0700 Subject: [PATCH 115/159] [PubGrub] Note that selected package version is backed by a provided library First step on the path to remove `ProvidedLibraryPackageContainer` and `PackageReference.providedLibrary` and fix a problem where a package backed by a provided library has to be always treated as "new" or "updated" by `precomputeResolution` which triggers full resolution. --- Sources/PackageGraph/BoundVersion.swift | 5 +- .../PubGrub/PubGrubDependencyResolver.swift | 35 +++++---- .../LibraryMetadata.swift | 6 +- .../SourceControlPackageContainer.swift | 2 +- .../Workspace/Workspace+Dependencies.swift | 2 +- .../DependencyResolverPerfTests.swift | 2 +- Tests/PackageGraphTests/PubgrubTests.swift | 71 ++++--------------- 7 files changed, 38 insertions(+), 85 deletions(-) diff --git a/Sources/PackageGraph/BoundVersion.swift b/Sources/PackageGraph/BoundVersion.swift index 7aa90a33ff6..459dee7f956 100644 --- a/Sources/PackageGraph/BoundVersion.swift +++ b/Sources/PackageGraph/BoundVersion.swift @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +import struct PackageModel.ProvidedLibrary import struct TSCUtility.Version /// A bound version for a package within an assignment. @@ -22,7 +23,7 @@ public enum BoundVersion: Equatable, Hashable { case excluded /// The version of the package to include. - case version(Version) + case version(Version, library: ProvidedLibrary? = nil) /// The package assignment is unversioned. case unversioned @@ -36,7 +37,7 @@ extension BoundVersion: CustomStringConvertible { switch self { case .excluded: return "excluded" - case .version(let version): + case .version(let version, _): return version.description case .unversioned: return "unversioned" diff --git a/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift b/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift index c576bde35cf..00f3f1b1013 100644 --- a/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift +++ b/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift @@ -231,35 +231,32 @@ public struct PubGrubDependencyResolver { continue } - var package = assignment.term.node.package + let package = assignment.term.node.package let boundVersion: BoundVersion switch assignment.term.requirement { case .exact(let version): - boundVersion = .version(version) + if let library = package.matchingPrebuiltLibrary(in: availableLibraries), + version == library.version + { + boundVersion = .version(version, library: library) + } else { + boundVersion = .version(version) + } case .range, .any, .empty, .ranges: throw InternalError("unexpected requirement value for assignment \(assignment.term)") } - if let library = package.matchingPrebuiltLibrary(in: availableLibraries), - boundVersion == .version(library.version) - { - guard case .remoteSourceControl(let url) = package.kind else { - throw InternalError("Matched provided library against invalid package: \(package)") - } - - package = .providedLibrary( - identity: package.identity, - origin: url, - path: library.location - ) - } - let products = assignment.term.node.productFilter - // TODO: replace with async/await when available - let container = try temp_await { self.provider.getContainer(for: package, completion: $0) } - let updatePackage = try container.underlying.loadPackageReference(at: boundVersion) + let updatePackage: PackageReference + if case .version(_, let library) = boundVersion, library != nil { + updatePackage = package + } else { + // TODO: replace with async/await when available + let container = try temp_await { self.provider.getContainer(for: package, completion: $0) } + updatePackage = try container.underlying.loadPackageReference(at: boundVersion) + } if var existing = flattenedAssignments[updatePackage] { guard existing.binding == boundVersion else { diff --git a/Sources/PackageModel/InstalledLibrariesSupport/LibraryMetadata.swift b/Sources/PackageModel/InstalledLibrariesSupport/LibraryMetadata.swift index bde7b74ecfc..4a0312b6e78 100644 --- a/Sources/PackageModel/InstalledLibrariesSupport/LibraryMetadata.swift +++ b/Sources/PackageModel/InstalledLibrariesSupport/LibraryMetadata.swift @@ -13,7 +13,7 @@ import Basics import struct TSCUtility.Version -public struct ProvidedLibrary { +public struct ProvidedLibrary: Hashable { public let location: AbsolutePath public let metadata: LibraryMetadata @@ -22,8 +22,8 @@ public struct ProvidedLibrary { } } -public struct LibraryMetadata: Decodable { - public enum Identity: Equatable, Decodable { +public struct LibraryMetadata: Hashable, Decodable { + public enum Identity: Hashable, Decodable { case packageIdentity(scope: String, name: String) case sourceControl(url: SourceControlURL) } diff --git a/Sources/Workspace/PackageContainer/SourceControlPackageContainer.swift b/Sources/Workspace/PackageContainer/SourceControlPackageContainer.swift index 6e634df9fd4..03f48dd0120 100644 --- a/Sources/Workspace/PackageContainer/SourceControlPackageContainer.swift +++ b/Sources/Workspace/PackageContainer/SourceControlPackageContainer.swift @@ -354,7 +354,7 @@ internal final class SourceControlPackageContainer: PackageContainer, CustomStri let revision: Revision var version: Version? switch boundVersion { - case .version(let v): + case .version(let v, _): guard let tag = try self.knownVersions()[v] else { throw StringError("unknown tag \(v)") } diff --git a/Sources/Workspace/Workspace+Dependencies.swift b/Sources/Workspace/Workspace+Dependencies.swift index 0fab90aa3f1..6490577d92b 100644 --- a/Sources/Workspace/Workspace+Dependencies.swift +++ b/Sources/Workspace/Workspace+Dependencies.swift @@ -1096,7 +1096,7 @@ extension Workspace { packageStateChanges[binding.package.identity] = (binding.package, .added(newState)) } - case .version(let version): + case .version(let version, _): let stateChange: PackageStateChange switch currentDependency?.state { case .sourceControlCheckout(.version(version, _)), diff --git a/Tests/PackageGraphPerformanceTests/DependencyResolverPerfTests.swift b/Tests/PackageGraphPerformanceTests/DependencyResolverPerfTests.swift index 9d92488bbf3..054cf84ea20 100644 --- a/Tests/PackageGraphPerformanceTests/DependencyResolverPerfTests.swift +++ b/Tests/PackageGraphPerformanceTests/DependencyResolverPerfTests.swift @@ -69,7 +69,7 @@ class DependencyResolverRealWorldPerfTests: XCTestCasePerf { switch resolver.solve(constraints: graph.constraints) { case .success(let result): let result: [(container: PackageReference, version: Version)] = result.compactMap { - guard case .version(let version) = $0.boundVersion else { + guard case .version(let version, _) = $0.boundVersion else { XCTFail("Unexpected result") return nil } diff --git a/Tests/PackageGraphTests/PubgrubTests.swift b/Tests/PackageGraphTests/PubgrubTests.swift index 8920d14211e..5e0f7e21ad4 100644 --- a/Tests/PackageGraphTests/PubgrubTests.swift +++ b/Tests/PackageGraphTests/PubgrubTests.swift @@ -2046,16 +2046,12 @@ final class PubGrubTestsBasicGraphs: XCTestCase { let result = resolver.solve(constraints: dependencies1) print(try result.get()) AssertResult(result, [ - ( - "foo", - .providedLibrary(.init("https://example.com/org/foo"), .init("/foo")), - .version(.init(stringLiteral: "1.0.0")) - ), + ("foo", .version(.init(stringLiteral: "1.0.0"), library: availableLibraries.first!)), ]) let result2 = resolver.solve(constraints: dependencies2) AssertResult(result2, [ - ("foo", fooRef.kind, .version(.init(stringLiteral: "1.2.0"))), + ("foo", .version(.init(stringLiteral: "1.2.0"))), ]) } @@ -2114,16 +2110,8 @@ final class PubGrubTestsBasicGraphs: XCTestCase { let result = resolver.solve(constraints: dependencies) AssertResult(result, [ - ( - "foo", - .providedLibrary(.init("https://example.com/org/foo"), .init("/foo")), - .version(.init(stringLiteral: "1.1.0")) - ), - ( - "target", - .localSourceControl("/target"), - .version(.init(stringLiteral: "2.0.0")) - ), + ("foo", .version(.init(stringLiteral: "1.1.0"), library: availableLibraries.first!)), + ("target", .version(.init(stringLiteral: "2.0.0"))), ]) } @@ -2167,8 +2155,8 @@ final class PubGrubTestsBasicGraphs: XCTestCase { let result = resolver.solve(constraints: dependencies) AssertResult(result, [ - ("foo", fooRef.kind, .version(.init(stringLiteral: "1.1.0"))), - ("bar", .localSourceControl("/bar"), .version(.init(stringLiteral: "1.0.0"))), + ("foo", .version(.init(stringLiteral: "1.1.0"))), + ("bar", .version(.init(stringLiteral: "1.0.0"))), ]) } } @@ -3312,22 +3300,6 @@ private func AssertBindings( _ packages: [(identity: PackageIdentity, version: BoundVersion)], file: StaticString = #file, line: UInt = #line -) { - AssertBindings( - bindings, - packages.map { - (identity: $0, kind: nil, version: $1) - }, - file: file, - line: line - ) -} - -private func AssertBindings( - _ bindings: [DependencyResolverBinding], - _ packages: [(identity: PackageIdentity, kind: PackageReference.Kind?, version: BoundVersion)], - file: StaticString = #file, - line: UInt = #line ) { if bindings.count > packages.count { let unexpectedBindings = bindings @@ -3346,15 +3318,7 @@ private func AssertBindings( } for package in packages { guard let binding = bindings.first(where: { - if $0.package.identity != package.identity { - return false - } - - if let kind = package.kind, $0.package.kind != kind { - return false - } - - return true + $0.package.identity == package.identity }) else { XCTFail("No binding found for \(package.identity).", file: file, line: line) continue @@ -3376,21 +3340,12 @@ private func AssertResult( _ packages: [(identifier: String, version: BoundVersion)], file: StaticString = #file, line: UInt = #line -) { - AssertResult(result, packages.map { ($0, nil, $1) }, file: file, line: line) -} - -private func AssertResult( - _ result: Result<[DependencyResolverBinding], Error>, - _ packages: [(identifier: String, kind: PackageReference.Kind?, version: BoundVersion)], - file: StaticString = #file, - line: UInt = #line ) { switch result { case .success(let bindings): AssertBindings( bindings, - packages.map { (PackageIdentity($0.identifier), $0.kind, $0.version) }, + packages.map { (PackageIdentity($0.identifier), $0.version) }, file: file, line: line ) @@ -3440,7 +3395,7 @@ public class MockContainer: PackageContainer { public func toolsVersionsAppropriateVersionsDescending() throws -> [Version] { var versions: [Version] = [] for version in self._versions.reversed() { - guard case .version(let v) = version else { continue } + guard case .version(let v, _) = version else { continue } versions.append(v) } return versions @@ -3449,7 +3404,7 @@ public class MockContainer: PackageContainer { public func versionsAscending() throws -> [Version] { var versions: [Version] = [] for version in self._versions { - guard case .version(let v) = version else { continue } + guard case .version(let v, _) = version else { continue } versions.append(v) } return versions @@ -3516,7 +3471,7 @@ public class MockContainer: PackageContainer { self._versions.append(version) self._versions = self._versions .sorted(by: { lhs, rhs -> Bool in - guard case .version(let lv) = lhs, case .version(let rv) = rhs else { + guard case .version(let lv, _) = lhs, case .version(let rv, _) = rhs else { return true } return lv < rv @@ -3571,7 +3526,7 @@ public class MockContainer: PackageContainer { let versions = dependencies.keys.compactMap(Version.init(_:)) self._versions = versions .sorted() - .map(BoundVersion.version) + .map { .version($0) } } } @@ -3742,7 +3697,7 @@ class DependencyGraphBuilder { let container = self .containers[packageReference.identity.description] ?? MockContainer(package: packageReference) - if case .version(let v) = version { + if case .version(let v, _) = version { container.versionsToolsVersions[v] = toolsVersion ?? container.toolsVersion } From 2ad3bf2fd19e96398436077bc7792d9a917ad40c Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 24 Apr 2024 14:03:41 -0700 Subject: [PATCH 116/159] [Workspace] NFC: Handle loading of provided library managed dependencies directly Instead of going through the ManifestLoader machinery and special cases in validator let's just produce mainfest directly. --- .../ManifestLoader+Validation.swift | 5 -- Sources/PackageLoading/ManifestLoader.swift | 60 ------------------- Sources/PackageModel/Manifest/Manifest.swift | 47 +++++++++++++++ Sources/Workspace/Workspace+Manifests.swift | 11 +++- 4 files changed, 55 insertions(+), 68 deletions(-) diff --git a/Sources/PackageLoading/ManifestLoader+Validation.swift b/Sources/PackageLoading/ManifestLoader+Validation.swift index 768286b05ef..af215dd5298 100644 --- a/Sources/PackageLoading/ManifestLoader+Validation.swift +++ b/Sources/PackageLoading/ManifestLoader+Validation.swift @@ -34,11 +34,6 @@ public struct ManifestValidator { /// Validate the provided manifest. public func validate() -> [Basics.Diagnostic] { - // Provided library manifest is synthesized by the package manager. - if case .providedLibrary = self.manifest.packageKind { - return [] - } - var diagnostics = [Basics.Diagnostic]() diagnostics += self.validateTargets() diff --git a/Sources/PackageLoading/ManifestLoader.swift b/Sources/PackageLoading/ManifestLoader.swift index bf8f55fda45..9424af7e617 100644 --- a/Sources/PackageLoading/ManifestLoader.swift +++ b/Sources/PackageLoading/ManifestLoader.swift @@ -206,19 +206,6 @@ extension ManifestLoaderProtocol { completion: @escaping (Result) -> Void ) { do { - if case .providedLibrary = packageKind { - let manifest = try self.loadLibrary( - fileSystem: fileSystem, - packagePath: packagePath, - packageKind: packageKind, - packageIdentity: packageIdentity, - packageLocation: packageLocation, - packageVersion: packageVersion?.version - ) - completion(.success(manifest)) - return - } - // find the manifest path and parse it's tools-version let manifestPath = try ManifestLoader.findManifest(packagePath: packagePath, fileSystem: fileSystem, currentToolsVersion: currentToolsVersion) let manifestToolsVersion = try ToolsVersionParser.parse(manifestPath: manifestPath, fileSystem: fileSystem) @@ -279,53 +266,6 @@ extension ManifestLoaderProtocol { ) } } - - private func loadLibrary( - fileSystem: FileSystem, - packagePath: AbsolutePath, - packageKind: PackageReference.Kind, - packageIdentity: PackageIdentity, - packageLocation: String, - packageVersion: Version? - ) throws -> Manifest { - let names = try fileSystem.getDirectoryContents(packagePath).filter { - $0.hasSuffix("swiftmodule") - }.map { - let components = $0.split(separator: ".") - return String(components[0]) - } - - let products: [ProductDescription] = try names.map { - try .init(name: $0, type: .library(.automatic), targets: [$0]) - } - - let targets: [TargetDescription] = try names.map { - try .init( - name: $0, - path: packagePath.pathString, - type: .providedLibrary - ) - } - - return .init( - displayName: packageIdentity.description, - path: packagePath.appending(component: "provided-libraries.json"), - packageKind: packageKind, - packageLocation: packageLocation, - defaultLocalization: nil, - platforms: [], - version: packageVersion, - revision: nil, - toolsVersion: .v6_0, - pkgConfig: nil, - providers: nil, - cLanguageStandard: nil, - cxxLanguageStandard: nil, - swiftLanguageVersions: nil, - products: products, - targets: targets - ) - } } // MARK: - ManifestLoader diff --git a/Sources/PackageModel/Manifest/Manifest.swift b/Sources/PackageModel/Manifest/Manifest.swift index 7556b79f7b9..73adb0802e0 100644 --- a/Sources/PackageModel/Manifest/Manifest.swift +++ b/Sources/PackageModel/Manifest/Manifest.swift @@ -554,3 +554,50 @@ extension Manifest: Encodable { try container.encode(self.packageKind, forKey: .packageKind) } } + +extension Manifest { + package static func forProvidedLibrary( + fileSystem: FileSystem, + package: PackageReference, + libraryPath: AbsolutePath, + version: Version + ) throws -> Manifest { + let names = try fileSystem.getDirectoryContents(libraryPath).filter { + $0.hasSuffix("swiftmodule") + }.map { + let components = $0.split(separator: ".") + return String(components[0]) + } + + let products: [ProductDescription] = try names.map { + try .init(name: $0, type: .library(.automatic), targets: [$0]) + } + + let targets: [TargetDescription] = try names.map { + try .init( + name: $0, + path: libraryPath.pathString, + type: .providedLibrary + ) + } + + return .init( + displayName: package.identity.description, + path: libraryPath.appending(component: "provided-library.json"), + packageKind: package.kind, + packageLocation: package.locationString, + defaultLocalization: nil, + platforms: [], + version: version, + revision: nil, + toolsVersion: .v6_0, + pkgConfig: nil, + providers: nil, + cLanguageStandard: nil, + cxxLanguageStandard: nil, + swiftLanguageVersions: nil, + products: products, + targets: targets + ) + } +} diff --git a/Sources/Workspace/Workspace+Manifests.swift b/Sources/Workspace/Workspace+Manifests.swift index 3f6f36c051b..511dd191d50 100644 --- a/Sources/Workspace/Workspace+Manifests.swift +++ b/Sources/Workspace/Workspace+Manifests.swift @@ -659,9 +659,14 @@ extension Workspace { case .registryDownload(let downloadedVersion): packageKind = managedDependency.packageRef.kind packageVersion = downloadedVersion - case .providedLibrary(_, let version): - packageKind = managedDependency.packageRef.kind - packageVersion = version + case .providedLibrary(let path, let version): + let manifest: Manifest? = try? .forProvidedLibrary( + fileSystem: fileSystem, + package: managedDependency.packageRef, + libraryPath: path, + version: version + ) + return completion(manifest) case .custom(let availableVersion, _): packageKind = managedDependency.packageRef.kind packageVersion = availableVersion From 0486f94c94c6bd65a5071dc8784e2ae1a62c5c20 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 24 Apr 2024 14:17:00 -0700 Subject: [PATCH 117/159] [Workspace] Introduce a new package state - `usesLibrary` All non-edited packages are eligible to use a provided library based on the dependency resolution information. `.usesLibrary` state would result in a new `.providedLibrary` managed dependency injected into workspace state instead of a regular local/remote package. --- Sources/Commands/PackageCommands/Update.swift | 2 +- Sources/Workspace/ManagedDependency.swift | 10 +-- .../Workspace/Workspace+Dependencies.swift | 63 +++++++++++-------- 3 files changed, 40 insertions(+), 35 deletions(-) diff --git a/Sources/Commands/PackageCommands/Update.swift b/Sources/Commands/PackageCommands/Update.swift index b4ced1bad0e..fa2586c2c4f 100644 --- a/Sources/Commands/PackageCommands/Update.swift +++ b/Sources/Commands/PackageCommands/Update.swift @@ -73,7 +73,7 @@ extension SwiftPackageCommand { case .removed: report += "\n" report += "- \(package.identity) \(currentVersion)" - case .unchanged: + case .unchanged, .usesLibrary: continue } } diff --git a/Sources/Workspace/ManagedDependency.swift b/Sources/Workspace/ManagedDependency.swift index 4f476c99965..5f2fd75cfc5 100644 --- a/Sources/Workspace/ManagedDependency.swift +++ b/Sources/Workspace/ManagedDependency.swift @@ -153,15 +153,11 @@ extension Workspace { public static func providedLibrary( packageRef: PackageReference, - version: Version + library: ProvidedLibrary ) throws -> ManagedDependency { - guard case .providedLibrary(_, let path) = packageRef.kind else { - throw InternalError("invalid package type: \(packageRef.kind)") - } - - return ManagedDependency( + ManagedDependency( packageRef: packageRef, - state: .providedLibrary(at: path, version: version), + state: .providedLibrary(at: library.location, version: library.version), subpath: try RelativePath(validating: packageRef.identity.description) ) } diff --git a/Sources/Workspace/Workspace+Dependencies.swift b/Sources/Workspace/Workspace+Dependencies.swift index 6490577d92b..f8b3f45b575 100644 --- a/Sources/Workspace/Workspace+Dependencies.swift +++ b/Sources/Workspace/Workspace+Dependencies.swift @@ -672,7 +672,7 @@ extension Workspace { metadata: packageRef.diagnosticsMetadata ).trap { switch state { - case .added, .updated, .unchanged: + case .added, .updated, .unchanged, .usesLibrary: break case .removed: try self.remove(package: packageRef) @@ -701,12 +701,27 @@ extension Workspace { productFilter: state.products, observabilityScope: observabilityScope ) - case .removed, .unchanged: + case .removed, .unchanged, .usesLibrary: break } } } + // Handle provided libraries + for (packageRef, state) in packageStateChanges { + observabilityScope.makeChildScope( + description: "adding provided libraries", + metadata: packageRef.diagnosticsMetadata + ).trap { + if case .usesLibrary(let library) = state { + try self.state.dependencies.add( + .providedLibrary(packageRef: packageRef, library: library) + ) + try self.state.save() + } + } + } + // Inform the delegate if nothing was updated. if packageStateChanges.filter({ $0.1 == .unchanged }).count == packageStateChanges.count { delegate?.dependenciesUpToDate() @@ -763,19 +778,6 @@ extension Workspace { state: .custom(version: version, path: path), subpath: RelativePath(validating: "") ) - self.state.dependencies.add(dependency) - try self.state.save() - return path - } else if let libraryContainer = container as? ProvidedLibraryPackageContainer { - guard case .providedLibrary(_, let path) = libraryContainer.package.kind else { - throw InternalError("invalid container for \(package.identity) of type \(package.kind)") - } - - let dependency: ManagedDependency = try .providedLibrary( - packageRef: libraryContainer.package, - version: version - ) - self.state.dependencies.add(dependency) try self.state.save() return path @@ -960,6 +962,9 @@ extension Workspace { /// The package is updated. case updated(State) + /// The package is replaced with a prebuilt library + case usesLibrary(ProvidedLibrary) + public var description: String { switch self { case .added(let requirement): @@ -970,15 +975,17 @@ extension Workspace { return "unchanged" case .updated(let requirement): return "updated(\(requirement))" + case .usesLibrary(let library): + return "usesLibrary(\(library.metadata.productName))" } } public var isAddedOrUpdated: Bool { switch self { case .added, .updated: - return true - case .unchanged, .removed: - return false + true + case .unchanged, .removed, .usesLibrary: + false } } } @@ -1096,18 +1103,20 @@ extension Workspace { packageStateChanges[binding.package.identity] = (binding.package, .added(newState)) } - case .version(let version, _): - let stateChange: PackageStateChange - switch currentDependency?.state { + case .version(let version, let library): + let stateChange: PackageStateChange = switch currentDependency?.state { case .sourceControlCheckout(.version(version, _)), - .registryDownload(version), - .providedLibrary(_, version: version), - .custom(version, _): - stateChange = .unchanged + .registryDownload(version), + .providedLibrary(_, version: version), + .custom(version, _): + library.flatMap { .usesLibrary($0) } ?? .unchanged case .edited, .fileSystem, .sourceControlCheckout, .registryDownload, .providedLibrary, .custom: - stateChange = .updated(.init(requirement: .version(version), products: binding.products)) + .updated(.init(requirement: .version(version), products: binding.products)) case nil: - stateChange = .added(.init(requirement: .version(version), products: binding.products)) + library.flatMap { .usesLibrary($0) } ?? .added(.init( + requirement: .version(version), + products: binding.products + )) } packageStateChanges[binding.package.identity] = (binding.package, stateChange) } From ec680ec3c5c6e7bec8166d08aaeae7921baece4c Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 24 Apr 2024 18:26:17 -0700 Subject: [PATCH 118/159] NFC: Remove `.providedLibrary` from PackageReference and its package container Dependency resolution is switched to use `BoundVersion` to carry provided library information, `.providedLibrary` is fully obsolete. --- Sources/Build/BuildPlan/BuildPlan.swift | 2 +- Sources/PackageGraph/CMakeLists.txt | 1 - Sources/PackageGraph/ModulesGraph.swift | 2 - .../ProvidedLibraryPackageContainer.swift | 75 ------------------- .../PubGrub/ContainerProvider.swift | 7 -- .../PubGrub/PubGrubDependencyResolver.swift | 2 +- .../PackageLoading/ManifestJSONParser.swift | 2 +- Sources/PackageModel/IdentityResolver.swift | 2 - Sources/PackageModel/PackageReference.swift | 26 ------- .../Plugins/PluginContextSerializer.swift | 2 - .../SPMTestSupport/MockManifestLoader.swift | 6 -- Sources/SPMTestSupport/MockWorkspace.swift | 2 - Sources/Workspace/Diagnostics.swift | 2 +- .../Workspace/Workspace+Dependencies.swift | 1 - .../Workspace+PackageContainer.swift | 9 --- Sources/Workspace/Workspace+State.swift | 38 ---------- Sources/Workspace/Workspace.swift | 2 - Tests/PackageGraphTests/PubgrubTests.swift | 12 --- .../PackageLoadingTests/PDLoadingTests.swift | 2 - 19 files changed, 4 insertions(+), 191 deletions(-) delete mode 100644 Sources/PackageGraph/ProvidedLibraryPackageContainer.swift diff --git a/Sources/Build/BuildPlan/BuildPlan.swift b/Sources/Build/BuildPlan/BuildPlan.swift index 7a74eb5cf3f..3fd34b1f65c 100644 --- a/Sources/Build/BuildPlan/BuildPlan.swift +++ b/Sources/Build/BuildPlan/BuildPlan.swift @@ -740,7 +740,7 @@ extension ResolvedPackage { switch self.underlying.manifest.packageKind { case .registry, .remoteSourceControl, .localSourceControl: return true - case .root, .fileSystem, .providedLibrary: + case .root, .fileSystem: return false } } diff --git a/Sources/PackageGraph/CMakeLists.txt b/Sources/PackageGraph/CMakeLists.txt index 13d010b11a7..b95b1eb9feb 100644 --- a/Sources/PackageGraph/CMakeLists.txt +++ b/Sources/PackageGraph/CMakeLists.txt @@ -20,7 +20,6 @@ add_library(PackageGraph PackageModel+Extensions.swift PackageRequirement.swift PinsStore.swift - ProvidedLibraryPackageContainer.swift Resolution/PubGrub/Assignment.swift Resolution/PubGrub/ContainerProvider.swift Resolution/PubGrub/DiagnosticReportBuilder.swift diff --git a/Sources/PackageGraph/ModulesGraph.swift b/Sources/PackageGraph/ModulesGraph.swift index 345724c2971..e37b0ab4d54 100644 --- a/Sources/PackageGraph/ModulesGraph.swift +++ b/Sources/PackageGraph/ModulesGraph.swift @@ -362,8 +362,6 @@ extension PackageGraphError: CustomStringConvertible { description += " (at '\(path)')" case .remoteSourceControl(let url): description += " (from '\(url)')" - case .providedLibrary(let url, let path): - description += " (from \(url) at \(path))" case .registry: break } diff --git a/Sources/PackageGraph/ProvidedLibraryPackageContainer.swift b/Sources/PackageGraph/ProvidedLibraryPackageContainer.swift deleted file mode 100644 index 73c3bf6252a..00000000000 --- a/Sources/PackageGraph/ProvidedLibraryPackageContainer.swift +++ /dev/null @@ -1,75 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift open source project -// -// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See http://swift.org/LICENSE.txt for license information -// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import Basics -import Dispatch -import PackageLoading -import PackageModel - -import struct TSCUtility.Version - -/// TODO: This could be removed once logic to handle provided libraries is integrated -/// into a \c PubGrubPackageContainer. -public struct ProvidedLibraryPackageContainer: PackageContainer { - public let package: PackageReference - - /// Observability scope to emit diagnostics - private let observabilityScope: ObservabilityScope - - public init( - package: PackageReference, - observabilityScope: ObservabilityScope - ) throws { - switch package.kind { - case .providedLibrary: - break - default: - throw InternalError("invalid package type \(package.kind)") - } - self.package = package - self.observabilityScope = observabilityScope.makeChildScope( - description: "ProvidedLibraryPackageContainer", - metadata: package.diagnosticsMetadata) - } - - public func isToolsVersionCompatible(at version: Version) -> Bool { - true - } - - public func toolsVersion(for version: Version) throws -> ToolsVersion { - .v4 - } - - public func toolsVersionsAppropriateVersionsDescending() throws -> [Version] { - return try versionsDescending() - } - - public func versionsAscending() throws -> [Version] { - [] - } - - public func getDependencies(at version: Version, productFilter: ProductFilter) throws -> [PackageContainerConstraint] { - [] - } - - public func getDependencies(at revision: String, productFilter: ProductFilter) throws -> [PackageContainerConstraint] { - [] - } - - public func getUnversionedDependencies(productFilter: ProductFilter) throws -> [PackageContainerConstraint] { - [] - } - - public func loadPackageReference(at boundVersion: BoundVersion) throws -> PackageReference { - package - } -} diff --git a/Sources/PackageGraph/Resolution/PubGrub/ContainerProvider.swift b/Sources/PackageGraph/Resolution/PubGrub/ContainerProvider.swift index dacf263937d..c20021d3642 100644 --- a/Sources/PackageGraph/Resolution/PubGrub/ContainerProvider.swift +++ b/Sources/PackageGraph/Resolution/PubGrub/ContainerProvider.swift @@ -98,13 +98,6 @@ final class ContainerProvider { let result = result.tryMap { container -> PubGrubPackageContainer in let pubGrubContainer = PubGrubPackageContainer(underlying: container, pins: self.pins) - // This container is not cached because it's intended to be transparent - // and requested only when forming final assignments. Caching it would - // mean that subsequent calls to `solve` would pick it up. - if case .providedLibrary = package.kind { - return pubGrubContainer - } - // only cache positive results self.containersCache[package] = pubGrubContainer return pubGrubContainer diff --git a/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift b/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift index 00f3f1b1013..c2e28f21713 100644 --- a/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift +++ b/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift @@ -895,7 +895,7 @@ extension PackageRequirement { extension PackageReference { public func matchingPrebuiltLibrary(in availableLibraries: [ProvidedLibrary]) -> ProvidedLibrary? { switch self.kind { - case .fileSystem, .localSourceControl, .root, .providedLibrary: + case .fileSystem, .localSourceControl, .root: return nil // can never match a prebuilt library case .registry(let identity): if let registryIdentity = identity.registry { diff --git a/Sources/PackageLoading/ManifestJSONParser.swift b/Sources/PackageLoading/ManifestJSONParser.swift index 3818c565828..564e5991982 100644 --- a/Sources/PackageLoading/ManifestJSONParser.swift +++ b/Sources/PackageLoading/ManifestJSONParser.swift @@ -83,7 +83,7 @@ enum ManifestJSONParser { case .localSourceControl(let _packagePath): // we have a more accurate path than the virtual one packagePath = _packagePath - case .root(let _packagePath), .fileSystem(let _packagePath), .providedLibrary(_, let _packagePath): + case .root(let _packagePath), .fileSystem(let _packagePath): // we dont have a more accurate path, and they should be the same // asserting (debug only) to make sure refactoring is correct 11/2023 assert(packagePath == _packagePath, "expecting package path '\(packagePath)' to be the same as '\(_packagePath)'") diff --git a/Sources/PackageModel/IdentityResolver.swift b/Sources/PackageModel/IdentityResolver.swift index 9755d7e26f3..847836ecf2d 100644 --- a/Sources/PackageModel/IdentityResolver.swift +++ b/Sources/PackageModel/IdentityResolver.swift @@ -46,8 +46,6 @@ public struct DefaultIdentityResolver: IdentityResolver { return try self.resolveIdentity(for: url) case .registry(let identity): return identity - case .providedLibrary(let url, _): - return try self.resolveIdentity(for: url) } } diff --git a/Sources/PackageModel/PackageReference.swift b/Sources/PackageModel/PackageReference.swift index 545a0f6ade8..d4cec83d3ac 100644 --- a/Sources/PackageModel/PackageReference.swift +++ b/Sources/PackageModel/PackageReference.swift @@ -34,9 +34,6 @@ public struct PackageReference { /// A package from a registry. case registry(PackageIdentity) - /// A prebuilt library provided by a toolchain for a package identified by the given "origin" URL. - case providedLibrary(SourceControlURL, AbsolutePath) - // FIXME: we should not need this once we migrate off URLs //@available(*, deprecated) public var locationString: String { @@ -52,8 +49,6 @@ public struct PackageReference { case .registry(let identity): // FIXME: this is a placeholder return identity.description - case .providedLibrary(let url, _): - return url.absoluteString } } @@ -75,8 +70,6 @@ public struct PackageReference { return "remoteSourceControl \(url)" case .registry(let identity): return "registry \(identity)" - case .providedLibrary(let url, let path): - return "provided library for \(url) @ \(path)" } } @@ -131,8 +124,6 @@ public struct PackageReference { case .registry(let identity): // FIXME: this is a placeholder self.deprecatedName = name ?? identity.description - case .providedLibrary(let url, _): - self.deprecatedName = name ?? PackageIdentityParser.computeDefaultName(fromURL: url) } } @@ -160,14 +151,6 @@ public struct PackageReference { public static func registry(identity: PackageIdentity) -> PackageReference { PackageReference(identity: identity, kind: .registry(identity)) } - - public static func providedLibrary( - identity: PackageIdentity, - origin: SourceControlURL, - path: AbsolutePath - ) -> PackageReference { - PackageReference(identity: identity, kind: .providedLibrary(origin, path)) - } } extension PackageReference: Equatable { @@ -187,11 +170,6 @@ extension PackageReference: Equatable { switch (self.kind, other.kind) { case (.remoteSourceControl(let lurl), .remoteSourceControl(let rurl)): return lurl.canonicalURL == rurl.canonicalURL - case (.remoteSourceControl(let lurl), .providedLibrary(let rurl, _)), - (.providedLibrary(let lurl, _), .remoteSourceControl(let rurl)): - return lurl.canonicalURL == rurl.canonicalURL - case (.providedLibrary(_, let lpath), .providedLibrary(_, let rpath)): - return lpath == rpath default: return true } @@ -246,10 +224,6 @@ extension PackageReference.Kind: Encodable { case .registry: var unkeyedContainer = container.nestedUnkeyedContainer(forKey: .registry) try unkeyedContainer.encode(self.isRoot) - case .providedLibrary(let url, let path): - var unkeyedContainer = container.nestedUnkeyedContainer(forKey: .providedLibrary) - try unkeyedContainer.encode(url) - try unkeyedContainer.encode(path) } } } diff --git a/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift b/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift index 9f84bbf9598..0ea946cb38e 100644 --- a/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift +++ b/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift @@ -240,8 +240,6 @@ internal struct PluginContextSerializer { return .repository(url: url.absoluteString, displayVersion: String(describing: package.manifest.version), scmRevision: String(describing: package.manifest.revision)) case .registry(let identity): return .registry(identity: identity.description, displayVersion: String(describing: package.manifest.version)) - case .providedLibrary(_, _): - throw InternalError("provided libraries are not supported in plugin context") } } diff --git a/Sources/SPMTestSupport/MockManifestLoader.swift b/Sources/SPMTestSupport/MockManifestLoader.swift index 70da34b5cc6..fc1f345ed64 100644 --- a/Sources/SPMTestSupport/MockManifestLoader.swift +++ b/Sources/SPMTestSupport/MockManifestLoader.swift @@ -109,9 +109,6 @@ extension ManifestLoader { packageIdentity = identity // FIXME: placeholder packageLocation = identity.description - case .providedLibrary(let url, let path): - packageIdentity = try identityResolver.resolveIdentity(for: url) - packageLocation = path.pathString } return try await self.load( manifestPath: manifestPath, @@ -159,9 +156,6 @@ extension ManifestLoader { packageIdentity = identity // FIXME: placeholder packageLocation = identity.description - case .providedLibrary(let url, let path): - packageIdentity = try identityResolver.resolveIdentity(for: url) - packageLocation = path.pathString } return try await self.load( packagePath: packagePath, diff --git a/Sources/SPMTestSupport/MockWorkspace.swift b/Sources/SPMTestSupport/MockWorkspace.swift index a09adf4079d..f2f24792322 100644 --- a/Sources/SPMTestSupport/MockWorkspace.swift +++ b/Sources/SPMTestSupport/MockWorkspace.swift @@ -1035,8 +1035,6 @@ extension PackageReference.Kind { return "remoteSourceControl" case .registry: return "registry" - case .providedLibrary: - return "library" } } } diff --git a/Sources/Workspace/Diagnostics.swift b/Sources/Workspace/Diagnostics.swift index aef1442693b..8bf918875bc 100644 --- a/Sources/Workspace/Diagnostics.swift +++ b/Sources/Workspace/Diagnostics.swift @@ -183,7 +183,7 @@ extension Basics.Diagnostic { switch $0.kind { case .registry(let identity): return "'\(identity.description)'" - case .remoteSourceControl(let url), .providedLibrary(let url, _): + case .remoteSourceControl(let url): return "'\($0.identity)' from \(url)" case .localSourceControl(let path), .fileSystem(let path), .root(let path): return "'\($0.identity)' at \(path)" diff --git a/Sources/Workspace/Workspace+Dependencies.swift b/Sources/Workspace/Workspace+Dependencies.swift index f8b3f45b575..5d494ba0252 100644 --- a/Sources/Workspace/Workspace+Dependencies.swift +++ b/Sources/Workspace/Workspace+Dependencies.swift @@ -33,7 +33,6 @@ import struct PackageGraph.ObservabilityDependencyResolverDelegate import struct PackageGraph.PackageContainerConstraint import struct PackageGraph.PackageGraphRoot import struct PackageGraph.PackageGraphRootInput -import struct PackageGraph.ProvidedLibraryPackageContainer import class PackageGraph.PinsStore import struct PackageGraph.PubGrubDependencyResolver import struct PackageGraph.Term diff --git a/Sources/Workspace/Workspace+PackageContainer.swift b/Sources/Workspace/Workspace+PackageContainer.swift index ca5e857b343..4787e3adef4 100644 --- a/Sources/Workspace/Workspace+PackageContainer.swift +++ b/Sources/Workspace/Workspace+PackageContainer.swift @@ -17,7 +17,6 @@ import enum PackageFingerprint.FingerprintCheckingMode import enum PackageGraph.ContainerUpdateStrategy import protocol PackageGraph.PackageContainer import protocol PackageGraph.PackageContainerProvider -import struct PackageGraph.ProvidedLibraryPackageContainer import struct PackageModel.PackageReference // MARK: - Package container provider @@ -94,14 +93,6 @@ extension Workspace: PackageContainerProvider { queue.async { completion(.success(container)) } - - case .providedLibrary: - let container = try ProvidedLibraryPackageContainer( - package: package, - observabilityScope: observabilityScope) - queue.async { - completion(.success(container)) - } } } catch { queue.async { diff --git a/Sources/Workspace/Workspace+State.swift b/Sources/Workspace/Workspace+State.swift index 8d827cb562f..4c4e971b16f 100644 --- a/Sources/Workspace/Workspace+State.swift +++ b/Sources/Workspace/Workspace+State.swift @@ -433,7 +433,6 @@ extension WorkspaceStateStorage { let kind: Kind let location: String let name: String - let originURL: String? init(_ reference: PackageModel.PackageReference) { self.identity = reference.identity.description @@ -441,28 +440,19 @@ extension WorkspaceStateStorage { case .root(let path): self.kind = .root self.location = path.pathString - self.originURL = nil case .fileSystem(let path): self.kind = .fileSystem self.location = path.pathString - self.originURL = nil case .localSourceControl(let path): self.kind = .localSourceControl self.location = path.pathString - self.originURL = nil case .remoteSourceControl(let url): self.kind = .remoteSourceControl self.location = url.absoluteString - self.originURL = nil case .registry: self.kind = .registry // FIXME: placeholder self.location = self.identity.description - self.originURL = nil - case .providedLibrary(let url, let path): - self.kind = .providedLibrary - self.originURL = url.absoluteString - self.location = path.pathString } self.name = reference.deprecatedName } @@ -473,7 +463,6 @@ extension WorkspaceStateStorage { case localSourceControl case remoteSourceControl case registry - case providedLibrary } } } @@ -516,14 +505,6 @@ extension PackageModel.PackageReference { kind = .remoteSourceControl(SourceControlURL(reference.location)) case .registry: kind = .registry(identity) - case .providedLibrary: - guard let url = reference.originURL else { - throw InternalError("Cannot form provided library reference without origin: \(reference)") - } - kind = try .providedLibrary( - SourceControlURL(url), - .init(validating: reference.location) - ) } self.init( @@ -791,7 +772,6 @@ extension WorkspaceStateStorage { let kind: Kind let location: String let name: String - let originURL: String? init(_ reference: PackageModel.PackageReference) { self.identity = reference.identity.description @@ -799,28 +779,19 @@ extension WorkspaceStateStorage { case .root(let path): self.kind = .root self.location = path.pathString - self.originURL = nil case .fileSystem(let path): self.kind = .fileSystem self.location = path.pathString - self.originURL = nil case .localSourceControl(let path): self.kind = .localSourceControl self.location = path.pathString - self.originURL = nil case .remoteSourceControl(let url): self.kind = .remoteSourceControl self.location = url.absoluteString - self.originURL = nil case .registry: self.kind = .registry // FIXME: placeholder self.location = self.identity.description - self.originURL = nil - case .providedLibrary(let url, let path): - self.kind = .providedLibrary - self.originURL = url.absoluteString - self.location = path.pathString } self.name = reference.deprecatedName } @@ -831,7 +802,6 @@ extension WorkspaceStateStorage { case localSourceControl case remoteSourceControl case registry - case providedLibrary } } } @@ -875,14 +845,6 @@ extension PackageModel.PackageReference { kind = .remoteSourceControl(SourceControlURL(reference.location)) case .registry: kind = .registry(identity) - case .providedLibrary: - guard let url = reference.originURL else { - throw InternalError("Cannot form a provided library reference without origin: \(reference)") - } - kind = try .providedLibrary( - SourceControlURL(url), - .init(validating: reference.location) - ) } self.init( diff --git a/Sources/Workspace/Workspace.swift b/Sources/Workspace/Workspace.swift index c27e9c5f523..26bcd60543c 100644 --- a/Sources/Workspace/Workspace.swift +++ b/Sources/Workspace/Workspace.swift @@ -1262,8 +1262,6 @@ extension Workspace { try self.removeRepository(dependency: dependencyToRemove) case .registry: try self.removeRegistryArchive(for: dependencyToRemove) - case .providedLibrary: - break // NOOP } // Save the state. diff --git a/Tests/PackageGraphTests/PubgrubTests.swift b/Tests/PackageGraphTests/PubgrubTests.swift index 5e0f7e21ad4..850ab22a48b 100644 --- a/Tests/PackageGraphTests/PubgrubTests.swift +++ b/Tests/PackageGraphTests/PubgrubTests.swift @@ -3554,18 +3554,6 @@ public struct MockProvider: PackageContainerProvider { ) -> Void ) { queue.async { - if case .providedLibrary(_, _) = package.kind { - do { - let container = try ProvidedLibraryPackageContainer( - package: package, - observabilityScope: observabilityScope - ) - return completion(.success(container)) - } catch { - return completion(.failure(error)) - } - } - completion( self.containersByIdentifier[package].map { .success($0) } ?? .failure(_MockLoadingError.unknownModule) diff --git a/Tests/PackageLoadingTests/PDLoadingTests.swift b/Tests/PackageLoadingTests/PDLoadingTests.swift index b8b04ef0e91..aa2bfd7af44 100644 --- a/Tests/PackageLoadingTests/PDLoadingTests.swift +++ b/Tests/PackageLoadingTests/PDLoadingTests.swift @@ -97,8 +97,6 @@ class PackageDescriptionLoadingTests: XCTestCase, ManifestLoaderDelegate { packagePath = path case .remoteSourceControl, .registry: packagePath = .root - case .providedLibrary(_, let path): - packagePath = path } let toolsVersion = toolsVersion From a76b970ae2cc40c110466d323f095095e443d7a1 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 25 Apr 2024 00:06:00 -0700 Subject: [PATCH 119/159] [Tests] NFC: Add build plan tests for provided libraries --- Tests/BuildTests/BuildPlanTests.swift | 108 ++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/Tests/BuildTests/BuildPlanTests.swift b/Tests/BuildTests/BuildPlanTests.swift index 6ce71ad87e5..24a4570325d 100644 --- a/Tests/BuildTests/BuildPlanTests.swift +++ b/Tests/BuildTests/BuildPlanTests.swift @@ -6260,4 +6260,112 @@ final class BuildPlanTests: XCTestCase { let dylibs = Array(buildProduct.dylibs.map({$0.product.name})).sorted() XCTAssertEqual(dylibs, ["BarLogging", "FooLogging"]) } + + func testSwiftPackageWithProvidedLibraries() throws { + let fs = InMemoryFileSystem( + emptyFiles: + "/A/Sources/ATarget/main.swift", + "/Libraries/B/BTarget.swiftmodule", + "/Libraries/C/CTarget.swiftmodule" + ) + + let observability = ObservabilitySystem.makeForTesting() + let graph = try loadModulesGraph( + fileSystem: fs, + manifests: [ + Manifest.createRootManifest( + displayName: "A", + path: "/A", + dependencies: [ + .localSourceControl(path: "/B", requirement: .upToNextMajor(from: "1.0.0")), + .localSourceControl(path: "/C", requirement: .upToNextMajor(from: "1.0.0")), + ], + products: [ + ProductDescription( + name: "A", + type: .executable, + targets: ["ATarget"] + ) + ], + targets: [ + TargetDescription(name: "ATarget", dependencies: ["BLibrary", "CLibrary"]) + ] + ), + Manifest.createFileSystemManifest( + displayName: "B", + path: "/B", + products: [ + ProductDescription(name: "BLibrary", type: .library(.automatic), targets: ["BTarget"]), + ], + targets: [ + TargetDescription( + name: "BTarget", + path: "/Libraries/B", + type: .providedLibrary + ) + ] + ), + Manifest.createFileSystemManifest( + displayName: "C", + path: "/C", + products: [ + ProductDescription(name: "CLibrary", type: .library(.automatic), targets: ["CTarget"]), + ], + targets: [ + TargetDescription( + name: "CTarget", + path: "/Libraries/C", + type: .providedLibrary + ) + ] + ), + ], + observabilityScope: observability.topScope + ) + + XCTAssertNoDiagnostics(observability.diagnostics) + + let plan = try BuildPlan( + buildParameters: mockBuildParameters(), + graph: graph, + fileSystem: fs, + observabilityScope: observability.topScope + ) + let result = try BuildPlanResult(plan: plan) + + result.checkProductsCount(1) + result.checkTargetsCount(1) + + XCTAssertMatch( + try result.target(for: "ATarget").swiftTarget().compileArguments(), + [ + .anySequence, + "-I", "/Libraries/C", + "-I", "/Libraries/B", + .anySequence + ] + ) + + let linkerArgs = try result.buildProduct(for: "A").linkArguments() + + XCTAssertMatch( + linkerArgs, + [ + .anySequence, + "-L", "/Libraries/B", + "-l", "BTarget", + .anySequence + ] + ) + + XCTAssertMatch( + linkerArgs, + [ + .anySequence, + "-L", "/Libraries/C", + "-l", "CTarget", + .anySequence + ] + ) + } } From 6a719f42d5ceb12254b6d1b6ad15ab6bb1310b5e Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 25 Apr 2024 13:52:03 -0700 Subject: [PATCH 120/159] [Workspace] Augment resolution based on Package.resolved to support provided libraries If a package with a provided library is mentioned in the Package.resolved let's check whether the version of the library matches pinned version of the package and is so, switch to the library. --- Sources/Workspace/Workspace+Dependencies.swift | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Sources/Workspace/Workspace+Dependencies.swift b/Sources/Workspace/Workspace+Dependencies.swift index 5d494ba0252..2e99b7d2107 100644 --- a/Sources/Workspace/Workspace+Dependencies.swift +++ b/Sources/Workspace/Workspace+Dependencies.swift @@ -371,6 +371,20 @@ extension Workspace { // automatically manage the parallelism. let group = DispatchGroup() for pin in pinsStore.pins.values { + // Provided library doesn't have a container, we need to inject a special depedency. + if let library = pin.packageRef.matchingPrebuiltLibrary(in: self.providedLibraries), + case .version(library.version, _) = pin.state + { + try self.state.dependencies.add( + .providedLibrary( + packageRef: pin.packageRef, + library: library + ) + ) + try self.state.save() + continue + } + group.enter() let observabilityScope = observabilityScope.makeChildScope( description: "requesting package containers", @@ -418,7 +432,9 @@ extension Workspace { return !pin.state.equals(checkoutState) case .registryDownload(let version): return !pin.state.equals(version) - case .edited, .fileSystem, .providedLibrary, .custom: + case .providedLibrary: + return false + case .edited, .fileSystem, .custom: return true } } From 71a722ce8a6327094b98b3180e5d6642c0cbbbe2 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 26 Apr 2024 21:59:10 +0100 Subject: [PATCH 121/159] Remove `experimental` prefix from Swift SDK command (#7507) Since [SE-0387](https://github.com/apple/swift-evolution/blob/main/proposals/0387-cross-compilation-destinations.md) was accepted and available in Swift 5.9 and Swift 5.10, it's time to remove `experimental` prefix. --- Package.swift | 2 +- Sources/CMakeLists.txt | 2 +- Sources/CoreCommands/Options.swift | 11 +++++++++-- .../PackageModel/SwiftSDKs/SwiftSDKBundleStore.swift | 2 +- Sources/SwiftSDKCommand/README.md | 4 ++-- Sources/SwiftSDKCommand/SwiftSDKCommand.swift | 2 +- Sources/swift-package-manager/SwiftPM.swift | 2 +- .../CMakeLists.txt | 8 ++++---- .../Entrypoint.swift | 0 Utilities/bootstrap | 2 +- 10 files changed, 21 insertions(+), 14 deletions(-) rename Sources/{swift-experimental-sdk => swift-sdk}/CMakeLists.txt (68%) rename Sources/{swift-experimental-sdk => swift-sdk}/Entrypoint.swift (100%) diff --git a/Package.swift b/Package.swift index b48a7debc47..d20e84920e0 100644 --- a/Package.swift +++ b/Package.swift @@ -529,7 +529,7 @@ let package = Package( ), .executableTarget( /** Interacts with Swift SDKs used for cross-compilation */ - name: "swift-experimental-sdk", + name: "swift-sdk", dependencies: ["Commands", "SwiftSDKCommand"], exclude: ["CMakeLists.txt"] ), diff --git a/Sources/CMakeLists.txt b/Sources/CMakeLists.txt index 1f4226f88e6..d32210375e8 100644 --- a/Sources/CMakeLists.txt +++ b/Sources/CMakeLists.txt @@ -32,7 +32,7 @@ add_subdirectory(SPMLLBuild) add_subdirectory(SPMSQLite3) add_subdirectory(swift-bootstrap) add_subdirectory(swift-build) -add_subdirectory(swift-experimental-sdk) +add_subdirectory(swift-sdk) add_subdirectory(swift-package) add_subdirectory(swift-run) add_subdirectory(swift-test) diff --git a/Sources/CoreCommands/Options.swift b/Sources/CoreCommands/Options.swift index bcb60853c23..8a2a38b3ee8 100644 --- a/Sources/CoreCommands/Options.swift +++ b/Sources/CoreCommands/Options.swift @@ -112,7 +112,11 @@ package struct LocationOptions: ParsableArguments { package var customCompileDestination: AbsolutePath? /// Path to the directory containing installed Swift SDKs. - @Option(name: .customLong("experimental-swift-sdks-path"), help: .hidden, completion: .directory) + @Option( + name: .customLong("swift-sdks-path"), + help: "Path to the directory containing installed Swift SDKs", + completion: .directory + ) package var swiftSDKsDirectory: AbsolutePath? @Option( @@ -407,7 +411,10 @@ package struct BuildOptions: ParsableArguments { package var architectures: [String] = [] /// Filter for selecting a specific Swift SDK to build with. - @Option(name: .customLong("experimental-swift-sdk"), help: .hidden) + @Option( + name: .customLong("swift-sdk"), + help: "Filter for selecting a specific Swift SDK to build with" + ) package var swiftSDKSelector: String? /// Which compile-time sanitizers should be enabled. diff --git a/Sources/PackageModel/SwiftSDKs/SwiftSDKBundleStore.swift b/Sources/PackageModel/SwiftSDKs/SwiftSDKBundleStore.swift index f5e40bb2083..13d3b55b966 100644 --- a/Sources/PackageModel/SwiftSDKs/SwiftSDKBundleStore.swift +++ b/Sources/PackageModel/SwiftSDKs/SwiftSDKBundleStore.swift @@ -46,7 +46,7 @@ public final class SwiftSDKBundleStore { case let .noMatchingSwiftSDK(selector, hostTriple): return """ No Swift SDK found matching query `\(selector)` and host triple \ - `\(hostTriple.tripleString)`. Use `swift experimental-sdk list` command to see \ + `\(hostTriple.tripleString)`. Use `swift sdk list` command to see \ available Swift SDKs. """ } diff --git a/Sources/SwiftSDKCommand/README.md b/Sources/SwiftSDKCommand/README.md index 15fb0924c16..94180372b7a 100644 --- a/Sources/SwiftSDKCommand/README.md +++ b/Sources/SwiftSDKCommand/README.md @@ -1,4 +1,4 @@ -# Swift SDKs Tool +# Swift SDKs Command -This module implements `swift experimental-sdk` command and its subcommands, which allow managing artifact +This module implements `swift sdk` command and its subcommands, which allow managing artifact bundles used as Swift SDKs, as specified in [SE-0387](https://github.com/apple/swift-evolution/blob/main/proposals/0387-cross-compilation-destinations.md). diff --git a/Sources/SwiftSDKCommand/SwiftSDKCommand.swift b/Sources/SwiftSDKCommand/SwiftSDKCommand.swift index dc0cf4fe933..e39740d4796 100644 --- a/Sources/SwiftSDKCommand/SwiftSDKCommand.swift +++ b/Sources/SwiftSDKCommand/SwiftSDKCommand.swift @@ -15,7 +15,7 @@ import Basics package struct SwiftSDKCommand: AsyncParsableCommand { package static let configuration = CommandConfiguration( - commandName: "experimental-sdk", + commandName: "sdk", _superCommandName: "swift", abstract: "Perform operations on Swift SDKs.", version: SwiftVersion.current.completeDisplayString, diff --git a/Sources/swift-package-manager/SwiftPM.swift b/Sources/swift-package-manager/SwiftPM.swift index 84ea0f28baa..9dd43be5f0b 100644 --- a/Sources/swift-package-manager/SwiftPM.swift +++ b/Sources/swift-package-manager/SwiftPM.swift @@ -41,7 +41,7 @@ struct SwiftPM { await SwiftPackageCommand.main() case "swift-build": await SwiftBuildCommand.main() - case "swift-experimental-sdk": + case "swift-sdk": await SwiftSDKCommand.main() case "swift-test": await SwiftTestCommand.main() diff --git a/Sources/swift-experimental-sdk/CMakeLists.txt b/Sources/swift-sdk/CMakeLists.txt similarity index 68% rename from Sources/swift-experimental-sdk/CMakeLists.txt rename to Sources/swift-sdk/CMakeLists.txt index edad12be83b..ee3be128bae 100644 --- a/Sources/swift-experimental-sdk/CMakeLists.txt +++ b/Sources/swift-sdk/CMakeLists.txt @@ -6,13 +6,13 @@ # See http://swift.org/LICENSE.txt for license information # See http://swift.org/CONTRIBUTORS.txt for Swift project authors -add_executable(swift-experimental-sdk +add_executable(swift-sdk Entrypoint.swift) -target_link_libraries(swift-experimental-sdk PRIVATE +target_link_libraries(swift-sdk PRIVATE SwiftSDKCommand) -target_compile_options(swift-experimental-sdk PRIVATE +target_compile_options(swift-sdk PRIVATE -parse-as-library) -install(TARGETS swift-experimental-sdk +install(TARGETS swift-sdk RUNTIME DESTINATION bin) diff --git a/Sources/swift-experimental-sdk/Entrypoint.swift b/Sources/swift-sdk/Entrypoint.swift similarity index 100% rename from Sources/swift-experimental-sdk/Entrypoint.swift rename to Sources/swift-sdk/Entrypoint.swift diff --git a/Utilities/bootstrap b/Utilities/bootstrap index 8af6ec7e6d0..8abe53b8c86 100755 --- a/Utilities/bootstrap +++ b/Utilities/bootstrap @@ -435,7 +435,7 @@ def install_swiftpm(prefix, args): # Install the swift-package-manager tool and create symlinks to it. cli_tool_dest = os.path.join(prefix, "bin") install_binary(args, "swift-package-manager", os.path.join(cli_tool_dest, "swift-package"), destination_is_directory=False) - for tool in ["swift-build", "swift-test", "swift-run", "swift-package-collection", "swift-package-registry", "swift-experimental-sdk"]: + for tool in ["swift-build", "swift-test", "swift-run", "swift-package-collection", "swift-package-registry", "swift-sdk"]: src = "swift-package" dest = os.path.join(cli_tool_dest, tool) note("Creating tool symlink from %s to %s" % (src, dest)) From 5a4c0240c771db9dab8b5b264a9d368dfe11f0fc Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Fri, 26 Apr 2024 17:15:16 -0400 Subject: [PATCH 122/159] Fix test targets that depend on macros and `swift-testing` (#7508) After #7353 landed, I noticed that the build products for test targets were not being emitted correctly. swift-testing and XCTest produce separate build products (with distinct names) but this wasn't happening as intended. It turns out that the changes to split `buildParameters` into `productsBuildParameters` and `toolsBuildParameters` weren't fully propagated to our testing infrastructure. I also noticed `SWIFT_PM_SUPPORTS_SWIFT_TESTING` wasn't being set correctly anymore (same root cause) although we've decided to ignore that flag over in swift-testing anyway (see https://github.com/apple/swift-testing/pull/376.) This regression caused build failures in swift-testing (e.g. [here](https://ci.swift.org/job/pr-swift-testing-macos/663/console)) with the telltale failure signature: > /Users/ec2-user/jenkins/workspace/pr-swift-testing-macos/branch-main/swift-testing/.build/x86_64-apple-macosx/debug/swift-testingPackageTests.xctest/Contents/MacOS/swift-testingPackageTests: /Users/ec2-user/jenkins/workspace/pr-swift-testing-macos/branch-main/swift-testing/.build/x86_64-apple-macosx/debug/swift-testingPackageTests.xctest/Contents/MacOS/swift-testingPackageTests: cannot execute binary file Which indicates that it thinks the filename for the swift-testing build product is the XCTest bundle's executable. This PR plumbs through the two build parameters arguments to everywhere in `swift test` and `swift build` that needs them and resolves the issue. --------- Co-authored-by: Max Desiatov --- Sources/Commands/SwiftBuildCommand.swift | 23 +++- Sources/Commands/SwiftTestCommand.swift | 103 ++++++++++-------- .../Commands/Utilities/TestingSupport.swift | 35 +++++- 3 files changed, 106 insertions(+), 55 deletions(-) diff --git a/Sources/Commands/SwiftBuildCommand.swift b/Sources/Commands/SwiftBuildCommand.swift index 6d74dd63c25..7310ba5bbfe 100644 --- a/Sources/Commands/SwiftBuildCommand.swift +++ b/Sources/Commands/SwiftBuildCommand.swift @@ -149,8 +149,7 @@ package struct SwiftBuildCommand: AsyncSwiftCommand { throw ExitCode.failure } if case .allIncludingTests = subset { - var buildParameters = try swiftCommandState.productsBuildParameters - for library in try options.testLibraryOptions.enabledTestingLibraries(swiftCommandState: swiftCommandState) { + func updateTestingParameters(of buildParameters: inout BuildParameters, library: BuildParameters.Testing.Library) { buildParameters.testingParameters = .init( configuration: buildParameters.configuration, targetTriple: buildParameters.triple, @@ -161,18 +160,30 @@ package struct SwiftBuildCommand: AsyncSwiftCommand { testEntryPointPath: globalOptions.build.testEntryPointPath, library: library ) - try build(swiftCommandState, subset: subset, buildParameters: buildParameters) + } + var productsBuildParameters = try swiftCommandState.productsBuildParameters + var toolsBuildParameters = try swiftCommandState.toolsBuildParameters + for library in try options.testLibraryOptions.enabledTestingLibraries(swiftCommandState: swiftCommandState) { + updateTestingParameters(of: &productsBuildParameters, library: library) + updateTestingParameters(of: &toolsBuildParameters, library: library) + try build(swiftCommandState, subset: subset, productsBuildParameters: productsBuildParameters, toolsBuildParameters: toolsBuildParameters) } } else { - try build(swiftCommandState, subset: subset) + try build(swiftCommandState, subset: subset, productsBuildParameters: nil, toolsBuildParameters: nil) } } - private func build(_ swiftCommandState: SwiftCommandState, subset: BuildSubset, buildParameters: BuildParameters? = nil) throws { + private func build( + _ swiftCommandState: SwiftCommandState, + subset: BuildSubset, + productsBuildParameters: BuildParameters?, + toolsBuildParameters: BuildParameters? + ) throws { let buildSystem = try swiftCommandState.createBuildSystem( explicitProduct: options.product, shouldLinkStaticSwiftStdlib: options.shouldLinkStaticSwiftStdlib, - productsBuildParameters: buildParameters, + productsBuildParameters: productsBuildParameters, + toolsBuildParameters: toolsBuildParameters, // command result output goes on stdout // ie "swift build" should output to stdout outputStream: TSCBasic.stdoutStream diff --git a/Sources/Commands/SwiftTestCommand.swift b/Sources/Commands/SwiftTestCommand.swift index afe0e9e6c45..a4d09edd0cd 100644 --- a/Sources/Commands/SwiftTestCommand.swift +++ b/Sources/Commands/SwiftTestCommand.swift @@ -218,11 +218,11 @@ package struct SwiftTestCommand: AsyncSwiftCommand { throw TestError.xcodeNotInstalled } - let buildParameters = try swiftCommandState.buildParametersForTest(options: self.options, library: .xctest) + let (productsBuildParameters, _) = try swiftCommandState.buildParametersForTest(options: self.options, library: .xctest) // Remove test output from prior runs and validate priors. - if self.options.enableExperimentalTestOutput && buildParameters.triple.supportsTestSummary { - _ = try? localFileSystem.removeFileTree(buildParameters.testOutputPath) + if self.options.enableExperimentalTestOutput && productsBuildParameters.triple.supportsTestSummary { + _ = try? localFileSystem.removeFileTree(productsBuildParameters.testOutputPath) } let testProducts = try buildTestsIfNeeded(swiftCommandState: swiftCommandState, library: .xctest) @@ -231,7 +231,7 @@ package struct SwiftTestCommand: AsyncSwiftCommand { try await runTestProducts( testProducts, additionalArguments: xctestArgs, - buildParameters: buildParameters, + productsBuildParameters: productsBuildParameters, swiftCommandState: swiftCommandState, library: .xctest ) @@ -258,7 +258,7 @@ package struct SwiftTestCommand: AsyncSwiftCommand { // Clean out the code coverage directory that may contain stale // profraw files from a previous run of the code coverage tool. if self.options.enableCodeCoverage { - try swiftCommandState.fileSystem.removeFileTree(buildParameters.codeCovPath) + try swiftCommandState.fileSystem.removeFileTree(productsBuildParameters.codeCovPath) } // Run the tests using the parallel runner. @@ -268,7 +268,7 @@ package struct SwiftTestCommand: AsyncSwiftCommand { toolchain: toolchain, numJobs: options.numberOfWorkers ?? ProcessInfo.processInfo.activeProcessorCount, buildOptions: globalOptions.build, - buildParameters: buildParameters, + productsBuildParameters: productsBuildParameters, shouldOutputSuccess: swiftCommandState.logLevel <= .info, observabilityScope: swiftCommandState.observabilityScope ) @@ -287,7 +287,7 @@ package struct SwiftTestCommand: AsyncSwiftCommand { } if self.options.enableExperimentalTestOutput, !runner.ranSuccessfully { - try Self.handleTestOutput(buildParameters: buildParameters, packagePath: testProducts[0].packagePath) + try Self.handleTestOutput(productsBuildParameters: productsBuildParameters, packagePath: testProducts[0].packagePath) } } } @@ -348,13 +348,13 @@ package struct SwiftTestCommand: AsyncSwiftCommand { // MARK: - swift-testing private func swiftTestingRun(_ swiftCommandState: SwiftCommandState) async throws { - let buildParameters = try swiftCommandState.buildParametersForTest(options: self.options, library: .swiftTesting) + let (productsBuildParameters, _) = try swiftCommandState.buildParametersForTest(options: self.options, library: .swiftTesting) let testProducts = try buildTestsIfNeeded(swiftCommandState: swiftCommandState, library: .swiftTesting) let additionalArguments = Array(CommandLine.arguments.dropFirst()) try await runTestProducts( testProducts, additionalArguments: additionalArguments, - buildParameters: buildParameters, + productsBuildParameters: productsBuildParameters, swiftCommandState: swiftCommandState, library: .swiftTesting ) @@ -390,20 +390,20 @@ package struct SwiftTestCommand: AsyncSwiftCommand { private func runTestProducts( _ testProducts: [BuiltTestProduct], additionalArguments: [String], - buildParameters: BuildParameters, + productsBuildParameters: BuildParameters, swiftCommandState: SwiftCommandState, library: BuildParameters.Testing.Library ) async throws { // Clean out the code coverage directory that may contain stale // profraw files from a previous run of the code coverage tool. if self.options.enableCodeCoverage { - try swiftCommandState.fileSystem.removeFileTree(buildParameters.codeCovPath) + try swiftCommandState.fileSystem.removeFileTree(productsBuildParameters.codeCovPath) } let toolchain = try swiftCommandState.getTargetToolchain() let testEnv = try TestingSupport.constructTestEnvironment( toolchain: toolchain, - destinationBuildParameters: buildParameters, + destinationBuildParameters: productsBuildParameters, sanitizers: globalOptions.build.sanitizers ) @@ -432,17 +432,17 @@ package struct SwiftTestCommand: AsyncSwiftCommand { } if self.options.enableExperimentalTestOutput, !ranSuccessfully { - try Self.handleTestOutput(buildParameters: buildParameters, packagePath: testProducts[0].packagePath) + try Self.handleTestOutput(productsBuildParameters: productsBuildParameters, packagePath: testProducts[0].packagePath) } } - private static func handleTestOutput(buildParameters: BuildParameters, packagePath: AbsolutePath) throws { - guard localFileSystem.exists(buildParameters.testOutputPath) else { + private static func handleTestOutput(productsBuildParameters: BuildParameters, packagePath: AbsolutePath) throws { + guard localFileSystem.exists(productsBuildParameters.testOutputPath) else { print("No existing test output found.") return } - let lines = try String(contentsOfFile: buildParameters.testOutputPath.pathString).components(separatedBy: "\n") + let lines = try String(contentsOfFile: productsBuildParameters.testOutputPath.pathString).components(separatedBy: "\n") let events = try lines.map { try JSONDecoder().decode(TestEventRecord.self, from: $0) } let caseEvents = events.compactMap { $0.caseEvent } @@ -486,10 +486,10 @@ package struct SwiftTestCommand: AsyncSwiftCommand { // Merge all the profraw files to produce a single profdata file. try mergeCodeCovRawDataFiles(swiftCommandState: swiftCommandState, library: library) - let buildParameters = try swiftCommandState.buildParametersForTest(options: self.options, library: library) + let (productsBuildParameters, _) = try swiftCommandState.buildParametersForTest(options: self.options, library: library) for product in testProducts { // Export the codecov data as JSON. - let jsonPath = buildParameters.codeCovAsJSONPath(packageName: rootManifest.displayName) + let jsonPath = productsBuildParameters.codeCovAsJSONPath(packageName: rootManifest.displayName) try exportCodeCovAsJSON(to: jsonPath, testBinary: product.binaryPath, swiftCommandState: swiftCommandState, library: library) } } @@ -500,18 +500,18 @@ package struct SwiftTestCommand: AsyncSwiftCommand { let llvmProf = try swiftCommandState.getTargetToolchain().getLLVMProf() // Get the profraw files. - let buildParameters = try swiftCommandState.buildParametersForTest(options: self.options, library: library) - let codeCovFiles = try swiftCommandState.fileSystem.getDirectoryContents(buildParameters.codeCovPath) + let (productsBuildParameters, _) = try swiftCommandState.buildParametersForTest(options: self.options, library: library) + let codeCovFiles = try swiftCommandState.fileSystem.getDirectoryContents(productsBuildParameters.codeCovPath) // Construct arguments for invoking the llvm-prof tool. var args = [llvmProf.pathString, "merge", "-sparse"] for file in codeCovFiles { - let filePath = buildParameters.codeCovPath.appending(component: file) + let filePath = productsBuildParameters.codeCovPath.appending(component: file) if filePath.extension == "profraw" { args.append(filePath.pathString) } } - args += ["-o", buildParameters.codeCovDataFile.pathString] + args += ["-o", productsBuildParameters.codeCovDataFile.pathString] try TSCBasic.Process.checkNonZeroExit(arguments: args) } @@ -525,11 +525,11 @@ package struct SwiftTestCommand: AsyncSwiftCommand { ) throws { // Export using the llvm-cov tool. let llvmCov = try swiftCommandState.getTargetToolchain().getLLVMCov() - let buildParameters = try swiftCommandState.buildParametersForTest(options: self.options, library: library) + let (productsBuildParameters, _) = try swiftCommandState.buildParametersForTest(options: self.options, library: library) let args = [ llvmCov.pathString, "export", - "-instr-profile=\(buildParameters.codeCovDataFile)", + "-instr-profile=\(productsBuildParameters.codeCovDataFile)", testBinary.pathString ] let result = try TSCBasic.Process.popen(arguments: args) @@ -548,10 +548,11 @@ package struct SwiftTestCommand: AsyncSwiftCommand { swiftCommandState: SwiftCommandState, library: BuildParameters.Testing.Library ) throws -> [BuiltTestProduct] { - let buildParameters = try swiftCommandState.buildParametersForTest(options: self.options, library: library) + let (productsBuildParameters, toolsBuildParameters) = try swiftCommandState.buildParametersForTest(options: self.options, library: library) return try Commands.buildTestsIfNeeded( swiftCommandState: swiftCommandState, - buildParameters: buildParameters, + productsBuildParameters: productsBuildParameters, + toolsBuildParameters: toolsBuildParameters, testProduct: self.options.sharedOptions.testProduct ) } @@ -601,8 +602,8 @@ extension SwiftTestCommand { guard let rootManifest = rootManifests.values.first else { throw StringError("invalid manifests at \(root.packages)") } - let buildParameters = try swiftCommandState.buildParametersForTest(enableCodeCoverage: true, library: .xctest) - print(buildParameters.codeCovAsJSONPath(packageName: rootManifest.displayName)) + let (productsBuildParameters, _) = try swiftCommandState.buildParametersForTest(enableCodeCoverage: true, library: .xctest) + print(productsBuildParameters.codeCovAsJSONPath(packageName: rootManifest.displayName)) } } @@ -613,7 +614,7 @@ extension SwiftTestCommand { func run(_ swiftCommandState: SwiftCommandState) throws { try SwiftTestCommand.handleTestOutput( - buildParameters: try swiftCommandState.productsBuildParameters, + productsBuildParameters: try swiftCommandState.productsBuildParameters, packagePath: localFileSystem.currentWorkingDirectory ?? .root // by definition runs in the current working directory ) } @@ -641,12 +642,16 @@ extension SwiftTestCommand { // MARK: - XCTest private func xctestRun(_ swiftCommandState: SwiftCommandState) throws { - let buildParameters = try swiftCommandState.buildParametersForTest( + let (productsBuildParameters, toolsBuildParameters) = try swiftCommandState.buildParametersForTest( enableCodeCoverage: false, shouldSkipBuilding: sharedOptions.shouldSkipBuilding, library: .xctest ) - let testProducts = try buildTestsIfNeeded(swiftCommandState: swiftCommandState, buildParameters: buildParameters) + let testProducts = try buildTestsIfNeeded( + swiftCommandState: swiftCommandState, + productsBuildParameters: productsBuildParameters, + toolsBuildParameters: toolsBuildParameters + ) let testSuites = try TestingSupport.getTestSuites( in: testProducts, swiftCommandState: swiftCommandState, @@ -665,20 +670,21 @@ extension SwiftTestCommand { // MARK: - swift-testing private func swiftTestingRun(_ swiftCommandState: SwiftCommandState) throws { - let buildParameters = try swiftCommandState.buildParametersForTest( + let (productsBuildParameters, toolsBuildParameters) = try swiftCommandState.buildParametersForTest( enableCodeCoverage: false, shouldSkipBuilding: sharedOptions.shouldSkipBuilding, library: .swiftTesting ) let testProducts = try buildTestsIfNeeded( swiftCommandState: swiftCommandState, - buildParameters: buildParameters + productsBuildParameters: productsBuildParameters, + toolsBuildParameters: toolsBuildParameters ) let toolchain = try swiftCommandState.getTargetToolchain() let testEnv = try TestingSupport.constructTestEnvironment( toolchain: toolchain, - destinationBuildParameters: buildParameters, + destinationBuildParameters: productsBuildParameters, sanitizers: globalOptions.build.sanitizers ) @@ -716,11 +722,13 @@ extension SwiftTestCommand { private func buildTestsIfNeeded( swiftCommandState: SwiftCommandState, - buildParameters: BuildParameters + productsBuildParameters: BuildParameters, + toolsBuildParameters: BuildParameters ) throws -> [BuiltTestProduct] { return try Commands.buildTestsIfNeeded( swiftCommandState: swiftCommandState, - buildParameters: buildParameters, + productsBuildParameters: productsBuildParameters, + toolsBuildParameters: toolsBuildParameters, testProduct: self.sharedOptions.testProduct ) } @@ -913,7 +921,7 @@ final class ParallelTestRunner { private let toolchain: UserToolchain private let buildOptions: BuildOptions - private let buildParameters: BuildParameters + private let productsBuildParameters: BuildParameters /// Number of tests to execute in parallel. private let numJobs: Int @@ -930,7 +938,7 @@ final class ParallelTestRunner { toolchain: UserToolchain, numJobs: Int, buildOptions: BuildOptions, - buildParameters: BuildParameters, + productsBuildParameters: BuildParameters, shouldOutputSuccess: Bool, observabilityScope: ObservabilityScope ) { @@ -957,7 +965,7 @@ final class ParallelTestRunner { } self.buildOptions = buildOptions - self.buildParameters = buildParameters + self.productsBuildParameters = productsBuildParameters assert(numJobs > 0, "num jobs should be > 0") } @@ -987,7 +995,7 @@ final class ParallelTestRunner { let testEnv = try TestingSupport.constructTestEnvironment( toolchain: self.toolchain, - destinationBuildParameters: self.buildParameters, + destinationBuildParameters: self.productsBuildParameters, sanitizers: self.buildOptions.sanitizers ) @@ -1054,7 +1062,7 @@ final class ParallelTestRunner { // Print test results. for test in processedTests.get() { - if (!test.success || shouldOutputSuccess) && !buildParameters.testingParameters.experimentalTestOutput { + if (!test.success || shouldOutputSuccess) && !productsBuildParameters.testingParameters.experimentalTestOutput { // command's result output goes on stdout // ie "swift test" should output to stdout print(test.output) @@ -1273,7 +1281,7 @@ extension SwiftCommandState { func buildParametersForTest( options: TestCommandOptions, library: BuildParameters.Testing.Library - ) throws -> BuildParameters { + ) throws -> (productsBuildParameters: BuildParameters, toolsBuildParameters: BuildParameters) { var result = try self.buildParametersForTest( enableCodeCoverage: options.enableCodeCoverage, enableTestability: options.enableTestableImports, @@ -1282,7 +1290,8 @@ extension SwiftCommandState { library: library ) if try options.testLibraryOptions.enableSwiftTestingLibrarySupport(swiftCommandState: self) { - result.flags.swiftCompilerFlags += ["-DSWIFT_PM_SUPPORTS_SWIFT_TESTING"] + result.productsBuildParameters.flags.swiftCompilerFlags += ["-DSWIFT_PM_SUPPORTS_SWIFT_TESTING"] + result.toolsBuildParameters.flags.swiftCompilerFlags += ["-DSWIFT_PM_SUPPORTS_SWIFT_TESTING"] } return result } @@ -1342,10 +1351,14 @@ private extension Basics.Diagnostic { /// - Returns: The paths to the build test products. private func buildTestsIfNeeded( swiftCommandState: SwiftCommandState, - buildParameters: BuildParameters, + productsBuildParameters: BuildParameters, + toolsBuildParameters: BuildParameters, testProduct: String? ) throws -> [BuiltTestProduct] { - let buildSystem = try swiftCommandState.createBuildSystem(productsBuildParameters: buildParameters) + let buildSystem = try swiftCommandState.createBuildSystem( + productsBuildParameters: productsBuildParameters, + toolsBuildParameters: toolsBuildParameters + ) let subset = testProduct.map(BuildSubset.product) ?? .allIncludingTests try buildSystem.build(subset: subset) diff --git a/Sources/Commands/Utilities/TestingSupport.swift b/Sources/Commands/Utilities/TestingSupport.swift index d582223fc08..fccd9e1e959 100644 --- a/Sources/Commands/Utilities/TestingSupport.swift +++ b/Sources/Commands/Utilities/TestingSupport.swift @@ -122,7 +122,7 @@ enum TestingSupport { shouldSkipBuilding: shouldSkipBuilding, experimentalTestOutput: experimentalTestOutput, library: .xctest - ), + ).productsBuildParameters, sanitizers: sanitizers ) @@ -137,7 +137,7 @@ enum TestingSupport { enableCodeCoverage: enableCodeCoverage, shouldSkipBuilding: shouldSkipBuilding, library: .xctest - ), + ).productsBuildParameters, sanitizers: sanitizers ) args = [path.description, "--dump-tests-json"] @@ -218,8 +218,35 @@ extension SwiftCommandState { shouldSkipBuilding: Bool = false, experimentalTestOutput: Bool = false, library: BuildParameters.Testing.Library - ) throws -> BuildParameters { - var parameters = try self.productsBuildParameters + ) throws -> (productsBuildParameters: BuildParameters, toolsBuildParameters: BuildParameters) { + let productsBuildParameters = buildParametersForTest( + modifying: try productsBuildParameters, + enableCodeCoverage: enableCodeCoverage, + enableTestability: enableTestability, + shouldSkipBuilding: shouldSkipBuilding, + experimentalTestOutput: experimentalTestOutput, + library: library + ) + let toolsBuildParameters = buildParametersForTest( + modifying: try toolsBuildParameters, + enableCodeCoverage: enableCodeCoverage, + enableTestability: enableTestability, + shouldSkipBuilding: shouldSkipBuilding, + experimentalTestOutput: experimentalTestOutput, + library: library + ) + return (productsBuildParameters, toolsBuildParameters) + } + + private func buildParametersForTest( + modifying parameters: BuildParameters, + enableCodeCoverage: Bool, + enableTestability: Bool?, + shouldSkipBuilding: Bool, + experimentalTestOutput: Bool, + library: BuildParameters.Testing.Library + ) -> BuildParameters { + var parameters = parameters var explicitlyEnabledDiscovery = false var explicitlySpecifiedPath: AbsolutePath? From ecf2e8ff52ed65c61fca9ad3f8bff617239920c0 Mon Sep 17 00:00:00 2001 From: Ben Barham Date: Sat, 27 Apr 2024 12:48:54 -0700 Subject: [PATCH 123/159] Cleanup addition of swift-syntax (#7505) --- BuildSupport/SwiftSyntax/CMakeLists.txt | 24 +++++++++++---------- Sources/PackageModelSyntax/CMakeLists.txt | 26 ++++++----------------- 2 files changed, 19 insertions(+), 31 deletions(-) diff --git a/BuildSupport/SwiftSyntax/CMakeLists.txt b/BuildSupport/SwiftSyntax/CMakeLists.txt index 09f4cd51d2c..e1d9decb5f3 100644 --- a/BuildSupport/SwiftSyntax/CMakeLists.txt +++ b/BuildSupport/SwiftSyntax/CMakeLists.txt @@ -1,14 +1,16 @@ include(FetchContent) -set(BUILD_SHARED_LIBS OFF) - -if(DEFINED SWIFTPM_PATH_TO_SWIFT_SYNTAX_SOURCE) - file(TO_CMAKE_PATH "${SWIFTPM_PATH_TO_SWIFT_SYNTAX_SOURCE}" swift_syntax_path) - FetchContent_Declare(SwiftSyntax - SOURCE_DIR "${swift_syntax_path}") -else() - FetchContent_Declare(SwiftSyntax - GIT_REPOSITORY https://github.com/apple/swift-syntax - GIT_TAG main) +find_package(SwiftSyntax CONFIG GLOBAL) +if(NOT SwiftSyntax_FOUND) + set(SWIFT_SYNTAX_INSTALL_TARGETS YES) + if(DEFINED SWIFTPM_PATH_TO_SWIFT_SYNTAX_SOURCE) + file(TO_CMAKE_PATH "${SWIFTPM_PATH_TO_SWIFT_SYNTAX_SOURCE}" swift_syntax_path) + FetchContent_Declare(SwiftSyntax + SOURCE_DIR "${swift_syntax_path}") + else() + FetchContent_Declare(SwiftSyntax + GIT_REPOSITORY https://github.com/apple/swift-syntax + GIT_TAG main) + endif() + FetchContent_MakeAvailable(SwiftSyntax) endif() -FetchContent_MakeAvailable(SwiftSyntax) diff --git a/Sources/PackageModelSyntax/CMakeLists.txt b/Sources/PackageModelSyntax/CMakeLists.txt index 556fd0c619b..cfab869efc1 100644 --- a/Sources/PackageModelSyntax/CMakeLists.txt +++ b/Sources/PackageModelSyntax/CMakeLists.txt @@ -24,12 +24,12 @@ target_link_libraries(PackageModelSyntax PUBLIC PackageLoading PackageModel - SwiftBasicFormat - SwiftDiagnostics - SwiftIDEUtils - SwiftParser - SwiftSyntax - SwiftSyntaxBuilder + SwiftSyntax::SwiftBasicFormat + SwiftSyntax::SwiftDiagnostics + SwiftSyntax::SwiftIDEUtils + SwiftSyntax::SwiftParser + SwiftSyntax::SwiftSyntax + SwiftSyntax::SwiftSyntaxBuilder ) # NOTE(compnerd) workaround for CMake not setting up include flags yet @@ -41,17 +41,3 @@ install(TARGETS PackageModelSyntax LIBRARY DESTINATION lib RUNTIME DESTINATION bin) set_property(GLOBAL APPEND PROPERTY SwiftPM_EXPORTS PackageModelSyntax) - -set(SWIFT_SYNTAX_MODULES - SwiftBasicFormat - SwiftParser - SwiftParserDiagnostics - SwiftDiagnostics - SwiftSyntax - SwiftSyntaxBuilder - SwiftIDEUtils -) -export(TARGETS ${SWIFT_SYNTAX_MODULES} - NAMESPACE SPMSwiftSyntax:: - FILE ${CMAKE_BINARY_DIR}/cmake/modules/SwiftSyntaxConfig.cmake - EXPORT_LINK_INTERFACE_LIBRARIES) \ No newline at end of file From b0b652c70dabe3b4d50281b7df24cbf1171474d8 Mon Sep 17 00:00:00 2001 From: Euan Harris Date: Mon, 29 Apr 2024 20:30:28 +0100 Subject: [PATCH 124/159] plugins: Pass correct dependency origin information to plugins (#7506) ### Motivation: SwiftPM sends information about the origin of each package dependency to the plugin process, but the `PluginContext` passed to the plugin function incorrectly shows all origins as `root`. ### Modifications: The necessary information is included correctly in the serialized structure sent to the plugin by SwiftPM. The startup wrapper code in the plugin process correctly deserializes it but does not copy it into the final `PluginContext` struct which is passed to the plugin function; instead, `origin` is hard-coded to `.root`. This change copies the dependency information into the final context struct. ### Result: Plugins receive correct information about package dependencies - `local`, with a local path, or `repository` with a URL, version number and revision hash. --- .../PackagePlugin/PluginContextDeserializer.swift | 12 +++++++++++- Tests/CommandsTests/PackageCommandTests.swift | 11 +++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/Sources/PackagePlugin/PluginContextDeserializer.swift b/Sources/PackagePlugin/PluginContextDeserializer.swift index 015bff9484e..716d8f56d5b 100644 --- a/Sources/PackagePlugin/PluginContextDeserializer.swift +++ b/Sources/PackagePlugin/PluginContextDeserializer.swift @@ -251,12 +251,22 @@ internal struct PluginContextDeserializer { } let products = try wirePackage.productIds.map { try self.product(for: $0) } let targets = try wirePackage.targetIds.map { try self.target(for: $0) } + let origin: PackageOrigin = switch wirePackage.origin { + case .root: + .root + case .local(let pathId): + try .local(path: url(for: pathId).path) + case .repository(let url, let displayVersion, let scmRevision): + .repository(url: url, displayVersion: displayVersion, scmRevision: scmRevision) + case .registry(let identity, let displayVersion): + .registry(identity: identity, displayVersion: displayVersion) + } let package = Package( id: wirePackage.identity, displayName: wirePackage.displayName, directory: Path(url: directory), directoryURL: directory, - origin: .root, + origin: origin, toolsVersion: toolsVersion, dependencies: dependencies, products: products, diff --git a/Tests/CommandsTests/PackageCommandTests.swift b/Tests/CommandsTests/PackageCommandTests.swift index 3dcad8f34d1..f67e148dac1 100644 --- a/Tests/CommandsTests/PackageCommandTests.swift +++ b/Tests/CommandsTests/PackageCommandTests.swift @@ -1890,6 +1890,11 @@ final class PackageCommandTests: CommandsTestCase { print(" \\(file.path): \\(file.type)") } } + + // Print out the dependencies so that we can check them. + for dependency in context.package.dependencies { + print(" dependency \\(dependency.package.displayName): \\(dependency.package.origin)") + } } } """ @@ -1986,6 +1991,12 @@ final class PackageCommandTests: CommandsTestCase { let (stdout, _) = try SwiftPM.Package.execute(["mycmd"], packagePath: packageDir) XCTAssertMatch(stdout, .contains("Initial working directory: \(workingDirectory)")) } + + // Check that information about the dependencies was properly sent to the plugin. + do { + let (stdout, _) = try SwiftPM.Package.execute(["mycmd", "--target", "MyLibrary"], packagePath: packageDir) + XCTAssertMatch(stdout, .contains("dependency HelperPackage: local")) + } } } From 4aa8d783ca707b079d428fc21c03090fceb150db Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 30 Apr 2024 00:06:54 +0100 Subject: [PATCH 125/159] Bring back `experimental-sdk` as deprecated for compatibility (#7512) There are enough users of this command and corresponding options on `swift build` in the wild. These should be deprecated first before removing. --- CHANGELOG.md | 7 +++++++ Package.swift | 6 ++++++ Sources/CMakeLists.txt | 1 + Sources/CoreCommands/Options.swift | 6 ++++++ Sources/CoreCommands/SwiftCommandState.swift | 15 +++++++++++-- Sources/swift-experimental-sdk/CMakeLists.txt | 18 ++++++++++++++++ .../swift-experimental-sdk/Entrypoint.swift | 21 +++++++++++++++++++ Sources/swift-package-manager/SwiftPM.swift | 3 +++ Utilities/bootstrap | 2 +- 9 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 Sources/swift-experimental-sdk/CMakeLists.txt create mode 100644 Sources/swift-experimental-sdk/Entrypoint.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 61f95911802..35fa173eaba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ Note: This is in reverse chronological order, so newer entries are added to the Swift 6.0 ----------- +* [#7507] + + `swift experimental-sdk` command is deprecated with `swift sdk` command replacing it. `--experimental-swift-sdk` and + `--experimental-swift-sdks-path` options on `swift build` are deprecated with replacements that don't have the + `experimental` prefix. + * [#7202] Package manifests can now access information about the Git repository the given package is in via the context object's @@ -403,3 +409,4 @@ Swift 3.0 [#7118]: https://github.com/apple/swift-package-manager/pull/7118 [#7201]: https://github.com/apple/swift-package-manager/pull/7201 [#7202]: https://github.com/apple/swift-package-manager/pull/7202 +[#7505]: https://github.com/apple/swift-package-manager/pull/7507 diff --git a/Package.swift b/Package.swift index d20e84920e0..f65164ea0b2 100644 --- a/Package.swift +++ b/Package.swift @@ -533,6 +533,12 @@ let package = Package( dependencies: ["Commands", "SwiftSDKCommand"], exclude: ["CMakeLists.txt"] ), + .executableTarget( + /** Deprecated command superseded by `swift-sdk` */ + name: "swift-experimental-sdk", + dependencies: ["Commands", "SwiftSDKCommand"], + exclude: ["CMakeLists.txt"] + ), .executableTarget( /** Runs package tests */ name: "swift-test", diff --git a/Sources/CMakeLists.txt b/Sources/CMakeLists.txt index d32210375e8..279084bb5c0 100644 --- a/Sources/CMakeLists.txt +++ b/Sources/CMakeLists.txt @@ -32,6 +32,7 @@ add_subdirectory(SPMLLBuild) add_subdirectory(SPMSQLite3) add_subdirectory(swift-bootstrap) add_subdirectory(swift-build) +add_subdirectory(swift-experimental-sdk) add_subdirectory(swift-sdk) add_subdirectory(swift-package) add_subdirectory(swift-run) diff --git a/Sources/CoreCommands/Options.swift b/Sources/CoreCommands/Options.swift index 8a2a38b3ee8..59def60256d 100644 --- a/Sources/CoreCommands/Options.swift +++ b/Sources/CoreCommands/Options.swift @@ -111,6 +111,9 @@ package struct LocationOptions: ParsableArguments { @Option(name: .customLong("destination"), help: .hidden, completion: .directory) package var customCompileDestination: AbsolutePath? + @Option(name: .customLong("experimental-swift-sdks-path"), help: .hidden, completion: .directory) + package var deprecatedSwiftSDKsDirectory: AbsolutePath? + /// Path to the directory containing installed Swift SDKs. @Option( name: .customLong("swift-sdks-path"), @@ -410,6 +413,9 @@ package struct BuildOptions: ParsableArguments { ) package var architectures: [String] = [] + @Option(name: .customLong("experimental-swift-sdk"), help: .hidden) + package var deprecatedSwiftSDKSelector: String? + /// Filter for selecting a specific Swift SDK to build with. @Option( name: .customLong("swift-sdk"), diff --git a/Sources/CoreCommands/SwiftCommandState.swift b/Sources/CoreCommands/SwiftCommandState.swift index b94ff2268cd..4eb2ad72156 100644 --- a/Sources/CoreCommands/SwiftCommandState.swift +++ b/Sources/CoreCommands/SwiftCommandState.swift @@ -350,8 +350,13 @@ package final class SwiftCommandState { fileSystem: fileSystem ) self.sharedCacheDirectory = try getSharedCacheDirectory(options: options, fileSystem: fileSystem) + if options.locations.deprecatedSwiftSDKsDirectory != nil { + self.observabilityScope.emit( + warning: "`--experimental-swift-sdks-path` is deprecated and will be removed in a future version of SwiftPM. Use `--swift-sdks-path` instead." + ) + } self.sharedSwiftSDKsDirectory = try fileSystem.getSharedSwiftSDKsDirectory( - explicitDirectory: options.locations.swiftSDKsDirectory + explicitDirectory: options.locations.swiftSDKsDirectory ?? options.locations.deprecatedSwiftSDKsDirectory ) // set global process logging handler @@ -820,6 +825,12 @@ package final class SwiftCommandState { do { let hostToolchain = try _hostToolchain.get() hostSwiftSDK = hostToolchain.swiftSDK + + if options.build.deprecatedSwiftSDKSelector != nil { + self.observabilityScope.emit( + warning: "`--experimental-swift-sdk` is deprecated and will be removed in a future version of SwiftPM. Use `--swift-sdk` instead." + ) + } swiftSDK = try SwiftSDK.deriveTargetSwiftSDK( hostSwiftSDK: hostSwiftSDK, hostTriple: hostToolchain.targetTriple, @@ -827,7 +838,7 @@ package final class SwiftCommandState { customCompileTriple: options.build.customCompileTriple, customCompileToolchain: options.build.customCompileToolchain, customCompileSDK: options.build.customCompileSDK, - swiftSDKSelector: options.build.swiftSDKSelector, + swiftSDKSelector: options.build.swiftSDKSelector ?? options.build.deprecatedSwiftSDKSelector, architectures: options.build.architectures, store: store, observabilityScope: self.observabilityScope, diff --git a/Sources/swift-experimental-sdk/CMakeLists.txt b/Sources/swift-experimental-sdk/CMakeLists.txt new file mode 100644 index 00000000000..edad12be83b --- /dev/null +++ b/Sources/swift-experimental-sdk/CMakeLists.txt @@ -0,0 +1,18 @@ +# This source file is part of the Swift open source project +# +# Copyright (c) 2023 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See http://swift.org/LICENSE.txt for license information +# See http://swift.org/CONTRIBUTORS.txt for Swift project authors + +add_executable(swift-experimental-sdk + Entrypoint.swift) +target_link_libraries(swift-experimental-sdk PRIVATE + SwiftSDKCommand) + +target_compile_options(swift-experimental-sdk PRIVATE + -parse-as-library) + +install(TARGETS swift-experimental-sdk + RUNTIME DESTINATION bin) diff --git a/Sources/swift-experimental-sdk/Entrypoint.swift b/Sources/swift-experimental-sdk/Entrypoint.swift new file mode 100644 index 00000000000..f908ff3ff10 --- /dev/null +++ b/Sources/swift-experimental-sdk/Entrypoint.swift @@ -0,0 +1,21 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import SwiftSDKCommand + +@main +struct Entrypoint { + static func main() async { + print("warning: `swift experimental-sdk` command is deprecated and will be removed in a future version of SwiftPM. Use `swift sdk` instead.") + await SwiftSDKCommand.main() + } +} diff --git a/Sources/swift-package-manager/SwiftPM.swift b/Sources/swift-package-manager/SwiftPM.swift index 9dd43be5f0b..fa6fc156023 100644 --- a/Sources/swift-package-manager/SwiftPM.swift +++ b/Sources/swift-package-manager/SwiftPM.swift @@ -41,6 +41,9 @@ struct SwiftPM { await SwiftPackageCommand.main() case "swift-build": await SwiftBuildCommand.main() + case "swift-experimental-sdk": + print("warning: `swift experimental-sdk` command is deprecated and will be removed in a future version of SwiftPM. Use `swift sdk` instead.") + fallthrough case "swift-sdk": await SwiftSDKCommand.main() case "swift-test": diff --git a/Utilities/bootstrap b/Utilities/bootstrap index 8abe53b8c86..ea00fad37d3 100755 --- a/Utilities/bootstrap +++ b/Utilities/bootstrap @@ -435,7 +435,7 @@ def install_swiftpm(prefix, args): # Install the swift-package-manager tool and create symlinks to it. cli_tool_dest = os.path.join(prefix, "bin") install_binary(args, "swift-package-manager", os.path.join(cli_tool_dest, "swift-package"), destination_is_directory=False) - for tool in ["swift-build", "swift-test", "swift-run", "swift-package-collection", "swift-package-registry", "swift-sdk"]: + for tool in ["swift-build", "swift-test", "swift-run", "swift-package-collection", "swift-package-registry", "swift-sdk", "swift-experimental-sdk"]: src = "swift-package" dest = os.path.join(cli_tool_dest, tool) note("Creating tool symlink from %s to %s" % (src, dest)) From 8cc63c191013ded164dce9aa032de7f8558232d5 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 29 Apr 2024 13:52:34 -0700 Subject: [PATCH 126/159] NFC: Fix a typo in a label of `ModuleError.duplicateModulesScmAndRegistry` --- Sources/PackageGraph/ModulesGraph+Loading.swift | 4 ++-- Sources/PackageLoading/PackageBuilder.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/PackageGraph/ModulesGraph+Loading.swift b/Sources/PackageGraph/ModulesGraph+Loading.swift index b72e3fb265e..b20ef4c3ee7 100644 --- a/Sources/PackageGraph/ModulesGraph+Loading.swift +++ b/Sources/PackageGraph/ModulesGraph+Loading.swift @@ -609,7 +609,7 @@ private func createResolvedPackages( case (.some(let registryIdentity), .none): observabilityScope.emit( ModuleError.duplicateModulesScmAndRegistry( - regsitryPackage: registryIdentity, + registryPackage: registryIdentity, scmPackage: potentiallyDuplicatePackage.key.package2.identity, targets: potentiallyDuplicatePackage.value ) @@ -617,7 +617,7 @@ private func createResolvedPackages( case (.none, .some(let registryIdentity)): observabilityScope.emit( ModuleError.duplicateModulesScmAndRegistry( - regsitryPackage: registryIdentity, + registryPackage: registryIdentity, scmPackage: potentiallyDuplicatePackage.key.package1.identity, targets: potentiallyDuplicatePackage.value ) diff --git a/Sources/PackageLoading/PackageBuilder.swift b/Sources/PackageLoading/PackageBuilder.swift index 23d1a4d2fbf..59d5b972402 100644 --- a/Sources/PackageLoading/PackageBuilder.swift +++ b/Sources/PackageLoading/PackageBuilder.swift @@ -87,7 +87,7 @@ public enum ModuleError: Swift.Error { /// Indicates several targets with the same name exist in a registry and scm package case duplicateModulesScmAndRegistry( - regsitryPackage: PackageIdentity.RegistryIdentity, + registryPackage: PackageIdentity.RegistryIdentity, scmPackage: PackageIdentity, targets: [String] ) From 656563f419a53971e49dcc6a5f4a8134b5fe47ae Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Wed, 1 May 2024 00:05:28 +0100 Subject: [PATCH 127/159] Add end-to-end tests for `sdk` and `experimental-sdk` commands (#7517) Previously these commands weren't covered with end-to-end tests. Now we verify that installation/listing/removal of a fixture Swift SDK succeeds, and also verify that `experimental-sdk` invocations issue a deprecation warning. --- Fixtures/SwiftSDKs/Package.swift | 1 + Sources/SPMTestSupport/SwiftPMProduct.swift | 6 + Tests/CommandsTests/SDKCommandTests.swift | 122 ++++++++++++++++++++ 3 files changed, 129 insertions(+) create mode 100644 Fixtures/SwiftSDKs/Package.swift create mode 100644 Tests/CommandsTests/SDKCommandTests.swift diff --git a/Fixtures/SwiftSDKs/Package.swift b/Fixtures/SwiftSDKs/Package.swift new file mode 100644 index 00000000000..fc9bc8d91c8 --- /dev/null +++ b/Fixtures/SwiftSDKs/Package.swift @@ -0,0 +1 @@ +// This empty file tells test fixture logic to copy this directory's content to the test case temp directory. diff --git a/Sources/SPMTestSupport/SwiftPMProduct.swift b/Sources/SPMTestSupport/SwiftPMProduct.swift index 3338b295be0..93bd49e4857 100644 --- a/Sources/SPMTestSupport/SwiftPMProduct.swift +++ b/Sources/SPMTestSupport/SwiftPMProduct.swift @@ -25,6 +25,8 @@ package enum SwiftPM { case Registry case Test case Run + case experimentalSDK + case sdk } extension SwiftPM { @@ -41,6 +43,10 @@ extension SwiftPM { return "swift-test" case .Run: return "swift-run" + case .experimentalSDK: + return "swift-experimental-sdk" + case .sdk: + return "swift-sdk" } } diff --git a/Tests/CommandsTests/SDKCommandTests.swift b/Tests/CommandsTests/SDKCommandTests.swift new file mode 100644 index 00000000000..0c74126d8ba --- /dev/null +++ b/Tests/CommandsTests/SDKCommandTests.swift @@ -0,0 +1,122 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Basics +import Commands +import SPMTestSupport +import XCTest + +import class TSCBasic.Process +import enum TSCBasic.ProcessEnv + +private let deprecationWarning = "warning: `swift experimental-sdk` command is deprecated and will be removed in a future version of SwiftPM. Use `swift sdk` instead." + +final class SDKCommandTests: CommandsTestCase { + func testUsage() throws { + for command in [SwiftPM.sdk, SwiftPM.experimentalSDK] { + let stdout = try command.execute(["-help"]).stdout + XCTAssert(stdout.contains("USAGE: swift sdk ") || stdout.contains("USAGE: swift sdk []"), "got stdout:\n" + stdout) + } + } + + func testVersion() throws { + for command in [SwiftPM.sdk, SwiftPM.experimentalSDK] { + let stdout = try command.execute(["--version"]).stdout + XCTAssert(stdout.contains("Swift Package Manager"), "got stdout:\n" + stdout) + } + } + + func testInstallSDK() throws { + for command in [SwiftPM.sdk, SwiftPM.experimentalSDK] { + try fixture(name: "SwiftSDKs") { fixturePath in + for bundle in ["test-sdk.artifactbundle.tar.gz", "test-sdk.artifactbundle.zip"] { + var (stdout, stderr) = try command.execute( + [ + "install", + "--swift-sdks-path", fixturePath.pathString, + fixturePath.appending(bundle).pathString + ] + ) + + if command == .experimentalSDK { + XCTAssertMatch(stdout, .contains(deprecationWarning)) + } + + // We only expect tool's output on the stdout stream. + XCTAssertMatch( + stdout, + .contains("\(bundle)` successfully installed as test-sdk.artifactbundle.") + ) + + XCTAssertEqual(stderr.count, 0) + + (stdout, stderr) = try command.execute( + ["list", "--swift-sdks-path", fixturePath.pathString]) + + if command == .experimentalSDK { + XCTAssertMatch(stdout, .contains(deprecationWarning)) + } + + // We only expect tool's output on the stdout stream. + XCTAssertMatch(stdout, .contains("test-artifact")) + XCTAssertEqual(stderr.count, 0) + + XCTAssertThrowsError(try command.execute( + [ + "install", + "--swift-sdks-path", fixturePath.pathString, + fixturePath.appending(bundle).pathString + ] + )) { error in + guard case SwiftPMError.executionFailure(_, _, let stderr) = error else { + XCTFail() + return + } + + XCTAssertTrue( + stderr.contains( + "Error: Swift SDK bundle with name `test-sdk.artifactbundle` is already installed. Can't install a new bundle with the same name." + ), + "got stderr: \(stderr)" + ) + } + + if command == .experimentalSDK { + XCTAssertMatch(stdout, .contains(deprecationWarning)) + } + + (stdout, stderr) = try command.execute( + ["remove", "--swift-sdks-path", fixturePath.pathString, "test-artifact"]) + + if command == .experimentalSDK { + XCTAssertMatch(stdout, .contains(deprecationWarning)) + } + + // We only expect tool's output on the stdout stream. + XCTAssertMatch(stdout, .contains("test-sdk.artifactbundle` was successfully removed from the file system.")) + XCTAssertEqual(stderr.count, 0) + + (stdout, stderr) = try command.execute( + ["list", "--swift-sdks-path", fixturePath.pathString]) + + if command == .experimentalSDK { + XCTAssertMatch(stdout, .contains(deprecationWarning)) + } + + // We only expect tool's output on the stdout stream. + XCTAssertNoMatch(stdout, .contains("test-artifact")) + XCTAssertEqual(stderr.count, 0) + } + } + } + } +} From fe28c7739fed7a8f16a4c654db12b5b9561cf551 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Tue, 30 Apr 2024 19:48:58 -0400 Subject: [PATCH 128/159] Add `swift build --enable-code-coverage` (#7518) This PR adds the `--enable-code-coverage` flag (from `swift test`) to `swift build` so that code coverage can be used in a two-stage build process (build, then execute later.) Resolves rdar://127309781. --- Sources/Commands/SwiftBuildCommand.swift | 26 +++++++++++++++++---- Tests/CommandsTests/BuildCommandTests.swift | 17 ++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/Sources/Commands/SwiftBuildCommand.swift b/Sources/Commands/SwiftBuildCommand.swift index 7310ba5bbfe..b6813895e26 100644 --- a/Sources/Commands/SwiftBuildCommand.swift +++ b/Sources/Commands/SwiftBuildCommand.swift @@ -73,6 +73,12 @@ struct BuildCommandOptions: ParsableArguments { @Flag(help: "Build both source and test targets") var buildTests: Bool = false + /// Whether to enable code coverage. + @Flag(name: .customLong("code-coverage"), + inversion: .prefixedEnableDisable, + help: "Enable code coverage") + var enableCodeCoverage: Bool = false + /// If the binary output path should be printed. @Flag(name: .customLong("show-bin-path"), help: "Print the binary output path") var shouldPrintBinPath: Bool = false @@ -148,6 +154,18 @@ package struct SwiftBuildCommand: AsyncSwiftCommand { guard let subset = options.buildSubset(observabilityScope: swiftCommandState.observabilityScope) else { throw ExitCode.failure } + + var productsBuildParameters = try swiftCommandState.productsBuildParameters + var toolsBuildParameters = try swiftCommandState.toolsBuildParameters + + // Clean out the code coverage directory that may contain stale + // profraw files from a previous run of the code coverage tool. + if self.options.enableCodeCoverage { + try swiftCommandState.fileSystem.removeFileTree(swiftCommandState.productsBuildParameters.codeCovPath) + productsBuildParameters.testingParameters.enableCodeCoverage = true + toolsBuildParameters.testingParameters.enableCodeCoverage = true + } + if case .allIncludingTests = subset { func updateTestingParameters(of buildParameters: inout BuildParameters, library: BuildParameters.Testing.Library) { buildParameters.testingParameters = .init( @@ -161,23 +179,21 @@ package struct SwiftBuildCommand: AsyncSwiftCommand { library: library ) } - var productsBuildParameters = try swiftCommandState.productsBuildParameters - var toolsBuildParameters = try swiftCommandState.toolsBuildParameters for library in try options.testLibraryOptions.enabledTestingLibraries(swiftCommandState: swiftCommandState) { updateTestingParameters(of: &productsBuildParameters, library: library) updateTestingParameters(of: &toolsBuildParameters, library: library) try build(swiftCommandState, subset: subset, productsBuildParameters: productsBuildParameters, toolsBuildParameters: toolsBuildParameters) } } else { - try build(swiftCommandState, subset: subset, productsBuildParameters: nil, toolsBuildParameters: nil) + try build(swiftCommandState, subset: subset, productsBuildParameters: productsBuildParameters, toolsBuildParameters: toolsBuildParameters) } } private func build( _ swiftCommandState: SwiftCommandState, subset: BuildSubset, - productsBuildParameters: BuildParameters?, - toolsBuildParameters: BuildParameters? + productsBuildParameters: BuildParameters, + toolsBuildParameters: BuildParameters ) throws { let buildSystem = try swiftCommandState.createBuildSystem( explicitProduct: options.product, diff --git a/Tests/CommandsTests/BuildCommandTests.swift b/Tests/CommandsTests/BuildCommandTests.swift index ea54f5a12da..b81616d9bcd 100644 --- a/Tests/CommandsTests/BuildCommandTests.swift +++ b/Tests/CommandsTests/BuildCommandTests.swift @@ -664,4 +664,21 @@ final class BuildCommandTests: CommandsTestCase { } } #endif + + func testCodeCoverage() throws { + // Test that no codecov directory is created if not specified when building. + try fixture(name: "Miscellaneous/TestDiscovery/Simple") { path in + let buildResult = try self.build(["--build-tests"], packagePath: path, cleanAfterward: false) + XCTAssertThrowsError(try SwiftPM.Test.execute(["--skip-build", "--enable-code-coverage"], packagePath: path)) + } + + // Test that enabling code coverage during building produces the expected folder. + try fixture(name: "Miscellaneous/TestDiscovery/Simple") { path in + let buildResult = try self.build(["--build-tests", "--enable-code-coverage"], packagePath: path, cleanAfterward: false) + try SwiftPM.Test.execute(["--skip-build", "--enable-code-coverage"], packagePath: path) + let codeCovPath = buildResult.binPath.appending("codecov") + let codeCovFiles = try localFileSystem.getDirectoryContents(codeCovPath) + XCTAssertGreaterThan(codeCovFiles.count, 0) + } + } } From 8db240186de97b01c2d2fb351b367bbdd43fe6b6 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Wed, 1 May 2024 19:02:42 +0100 Subject: [PATCH 129/159] Fix incorrect paths for library products with explicit linkage (#7519) Library products declared with `.static` or `.dynamic` linkage didn't have a `-tools` suffix in their file names when built as dependencies of macros. This change resolves the inconsistency and adds corresponding build plan tests. The regression in question broke the compatibility test suite, which had [an old version of `swift-power-assert`](https://github.com/kishikawakatsumi/swift-power-assert/blob/a60cb50/Package.swift) depending on an old version of `swift-syntax` with explicit `.static` linkage for its products. rdar://127170225 --- .../BuildParameters/BuildParameters.swift | 4 +- .../SPMTestSupport/MockPackageGraphs.swift | 68 ++++++++++++++++++- .../CrossCompilationBuildPlanTests.swift | 53 +++++++++++++-- .../CrossCompilationPackageGraphTests.swift | 2 +- 4 files changed, 116 insertions(+), 11 deletions(-) diff --git a/Sources/SPMBuildCore/BuildParameters/BuildParameters.swift b/Sources/SPMBuildCore/BuildParameters/BuildParameters.swift index 02902339324..baf1e966d1a 100644 --- a/Sources/SPMBuildCore/BuildParameters/BuildParameters.swift +++ b/Sources/SPMBuildCore/BuildParameters/BuildParameters.swift @@ -243,7 +243,7 @@ public struct BuildParameters: Encodable { /// Returns the path to the dynamic library of a product for the current build parameters. func potentialDynamicLibraryPath(for product: ResolvedProduct) throws -> RelativePath { - try RelativePath(validating: "\(self.triple.dynamicLibraryPrefix)\(product.name)\(self.triple.dynamicLibraryExtension)") + try RelativePath(validating: "\(self.triple.dynamicLibraryPrefix)\(product.name)\(self.suffix(triple: product.buildTriple))\(self.triple.dynamicLibraryExtension)") } /// Returns the path to the binary of a product for the current build parameters, relative to the build directory. @@ -254,7 +254,7 @@ public struct BuildParameters: Encodable { case .executable, .snippet: return potentialExecutablePath case .library(.static): - return try RelativePath(validating: "lib\(product.name)\(self.triple.staticLibraryExtension)") + return try RelativePath(validating: "lib\(product.name)\(self.suffix(triple: product.buildTriple))\(self.triple.staticLibraryExtension)") case .library(.dynamic): return try potentialDynamicLibraryPath(for: product) case .library(.automatic), .plugin: diff --git a/Sources/SPMTestSupport/MockPackageGraphs.swift b/Sources/SPMTestSupport/MockPackageGraphs.swift index 00e12f77c19..df828675119 100644 --- a/Sources/SPMTestSupport/MockPackageGraphs.swift +++ b/Sources/SPMTestSupport/MockPackageGraphs.swift @@ -21,6 +21,7 @@ import func PackageGraph.loadModulesGraph import class PackageModel.Manifest import struct PackageModel.ProductDescription +import enum PackageModel.ProductType import struct PackageModel.TargetDescription import protocol TSCBasic.FileSystem import class TSCBasic.InMemoryFileSystem @@ -258,7 +259,7 @@ package func macrosTestsPackageGraph() throws -> MockPackageGraph { return (graph, fs, observability.topScope) } -package func trivialPackageGraph(pkgRootPath: AbsolutePath) throws -> MockPackageGraph { +package func trivialPackageGraph() throws -> MockPackageGraph { let fs = InMemoryFileSystem( emptyFiles: "/Pkg/Sources/app/main.swift", @@ -288,7 +289,7 @@ package func trivialPackageGraph(pkgRootPath: AbsolutePath) throws -> MockPackag return (graph, fs, observability.topScope) } -package func embeddedCxxInteropPackageGraph(pkgRootPath: AbsolutePath) throws -> MockPackageGraph { +package func embeddedCxxInteropPackageGraph() throws -> MockPackageGraph { let fs = InMemoryFileSystem( emptyFiles: "/Pkg/Sources/app/main.swift", @@ -329,3 +330,66 @@ package func embeddedCxxInteropPackageGraph(pkgRootPath: AbsolutePath) throws -> return (graph, fs, observability.topScope) } + +package func toolsExplicitLibrariesGraph(linkage: ProductType.LibraryType) throws -> MockPackageGraph { + let fs = InMemoryFileSystem(emptyFiles: + "/swift-mmio/Sources/MMIOMacros/source.swift", + "/swift-mmio/Sources/MMIOMacrosTests/source.swift", + "/swift-syntax/Sources/SwiftSyntax/source.swift" + ) + + let observability = ObservabilitySystem.makeForTesting() + let graph = try loadModulesGraph( + fileSystem: fs, + manifests: [ + Manifest.createRootManifest( + displayName: "swift-mmio", + path: "/swift-mmio", + dependencies: [ + .localSourceControl( + path: "/swift-syntax", + requirement: .upToNextMajor(from: "1.0.0") + ) + ], + targets: [ + TargetDescription( + name: "MMIOMacros", + dependencies: [ + .product(name: "SwiftSyntax", package: "swift-syntax"), + ], + type: .macro + ), + TargetDescription( + name: "MMIOMacrosTests", + dependencies: [ + .target(name: "MMIOMacros"), + ], + type: .test + ) + ] + ), + Manifest.createFileSystemManifest( + displayName: "swift-syntax", + path: "/swift-syntax", + products: [ + ProductDescription( + name: "SwiftSyntax", + type: .library(linkage), + targets: ["SwiftSyntax"] + ), + ], + targets: [ + TargetDescription( + name: "SwiftSyntax", + dependencies: [] + ), + ] + ), + ], + observabilityScope: observability.topScope + ) + + XCTAssertNoDiagnostics(observability.diagnostics) + + return (graph, fs, observability.topScope) +} diff --git a/Tests/BuildTests/CrossCompilationBuildPlanTests.swift b/Tests/BuildTests/CrossCompilationBuildPlanTests.swift index 4ac13002143..34ad1c16269 100644 --- a/Tests/BuildTests/CrossCompilationBuildPlanTests.swift +++ b/Tests/BuildTests/CrossCompilationBuildPlanTests.swift @@ -20,12 +20,14 @@ import struct Basics.Triple import enum PackageGraph.BuildTriple import class PackageModel.Manifest import struct PackageModel.TargetDescription +import enum PackageModel.ProductType import func SPMTestSupport.loadPackageGraph import func SPMTestSupport.embeddedCxxInteropPackageGraph import func SPMTestSupport.macrosPackageGraph import func SPMTestSupport.macrosTestsPackageGraph import func SPMTestSupport.mockBuildParameters +import func SPMTestSupport.toolsExplicitLibrariesGraph import func SPMTestSupport.trivialPackageGraph import struct SPMTestSupport.BuildPlanResult @@ -37,7 +39,7 @@ import XCTest final class CrossCompilationBuildPlanTests: XCTestCase { func testEmbeddedWasmTarget() throws { - var (graph, fs, observabilityScope) = try trivialPackageGraph(pkgRootPath: "/Pkg") + var (graph, fs, observabilityScope) = try trivialPackageGraph() let triple = try Triple("wasm32-unknown-none-wasm") var parameters = mockBuildParameters(triple: triple) @@ -68,7 +70,7 @@ final class CrossCompilationBuildPlanTests: XCTestCase { ] ) - (graph, fs, observabilityScope) = try embeddedCxxInteropPackageGraph(pkgRootPath: "/Pkg") + (graph, fs, observabilityScope) = try embeddedCxxInteropPackageGraph() result = try BuildPlanResult(plan: BuildPlan( buildParameters: parameters, @@ -98,9 +100,7 @@ final class CrossCompilationBuildPlanTests: XCTestCase { } func testWasmTargetRelease() throws { - let pkgPath = AbsolutePath("/Pkg") - - let (graph, fs, observabilityScope) = try trivialPackageGraph(pkgRootPath: pkgPath) + let (graph, fs, observabilityScope) = try trivialPackageGraph() var parameters = mockBuildParameters( config: .release, triple: .wasi, linkerDeadStrip: true @@ -133,7 +133,7 @@ final class CrossCompilationBuildPlanTests: XCTestCase { func testWASITarget() throws { let pkgPath = AbsolutePath("/Pkg") - let (graph, fs, observabilityScope) = try trivialPackageGraph(pkgRootPath: pkgPath) + let (graph, fs, observabilityScope) = try trivialPackageGraph() var parameters = mockBuildParameters(triple: .wasi) parameters.linkingParameters.shouldLinkStaticSwiftStdlib = true @@ -306,6 +306,47 @@ final class CrossCompilationBuildPlanTests: XCTestCase { ] ) } + + func testToolsExplicitLibraries() throws { + let destinationTriple = Triple.arm64Linux + let toolsTriple = Triple.x86_64MacOS + + for (linkage, productFileName) in [(ProductType.LibraryType.static, "libSwiftSyntax-tool.a"), (.dynamic, "libSwiftSyntax-tool.dylib")] { + let (graph, fs, scope) = try toolsExplicitLibrariesGraph(linkage: linkage) + let plan = try BuildPlan( + destinationBuildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true, triple: destinationTriple), + toolsBuildParameters: mockBuildParameters(triple: toolsTriple), + graph: graph, + fileSystem: fs, + observabilityScope: scope + ) + let result = try BuildPlanResult(plan: plan) + result.checkProductsCount(4) + result.checkTargetsCount(6) + + XCTAssertTrue(try result.allTargets(named: "SwiftSyntax") + .map { try $0.swiftTarget() } + .contains { $0.target.buildTriple == .tools }) + + try result.check(buildTriple: .tools, triple: toolsTriple, for: "swift-mmioPackageTests") + try result.check(buildTriple: .tools, triple: toolsTriple, for: "swift-mmioPackageDiscoveredTests") + try result.check(buildTriple: .tools, triple: toolsTriple, for: "MMIOMacros") + try result.check(buildTriple: .tools, triple: toolsTriple, for: "MMIOMacrosTests") + + let macroProducts = result.allProducts(named: "MMIOMacros") + XCTAssertEqual(macroProducts.count, 1) + let macroProduct = try XCTUnwrap(macroProducts.first) + XCTAssertEqual(macroProduct.buildParameters.triple, toolsTriple) + + let swiftSyntaxProducts = result.allProducts(named: "SwiftSyntax") + XCTAssertEqual(swiftSyntaxProducts.count, 2) + let swiftSyntaxToolsProduct = try XCTUnwrap(swiftSyntaxProducts.first { $0.product.buildTriple == .tools }) + let archiveArguments = try swiftSyntaxToolsProduct.archiveArguments() + + // Verify that produced library file has a correct name + XCTAssertMatch(archiveArguments, [.contains(productFileName)]) + } + } } extension BuildPlanResult { diff --git a/Tests/PackageGraphTests/CrossCompilationPackageGraphTests.swift b/Tests/PackageGraphTests/CrossCompilationPackageGraphTests.swift index dc50bab88c7..69c19852f59 100644 --- a/Tests/PackageGraphTests/CrossCompilationPackageGraphTests.swift +++ b/Tests/PackageGraphTests/CrossCompilationPackageGraphTests.swift @@ -20,7 +20,7 @@ import XCTest final class CrossCompilationPackageGraphTests: XCTestCase { func testTrivialPackage() throws { - let graph = try trivialPackageGraph(pkgRootPath: "/Pkg").graph + let graph = try trivialPackageGraph().graph try PackageGraphTester(graph) { result in result.check(packages: "Pkg") // "SwiftSyntax" is included for both host and target triples and is not pruned on this level From 9aa348e8eecc44fb6f93e1ef46e6dbd29947f4e7 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Wed, 1 May 2024 14:56:02 -0700 Subject: [PATCH 130/159] Report whether a target is part of the root package in the sourcekit-lsp API (#7492) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is used so we don’t search for tests in targets of package dependencies. Companion of https://github.com/apple/sourcekit-lsp/pull/1201 rdar://126965614 --- .../SourceKitLSPAPI/BuildDescription.swift | 42 +++++++++++++++---- .../PluginTargetBuildDescription.swift | 4 +- .../SourceKitLSPAPITests.swift | 12 ++++-- 3 files changed, 44 insertions(+), 14 deletions(-) diff --git a/Sources/SourceKitLSPAPI/BuildDescription.swift b/Sources/SourceKitLSPAPI/BuildDescription.swift index 5cdec76454f..af012d12802 100644 --- a/Sources/SourceKitLSPAPI/BuildDescription.swift +++ b/Sources/SourceKitLSPAPI/BuildDescription.swift @@ -22,21 +22,33 @@ import class Build.BuildPlan import class Build.ClangTargetBuildDescription import class Build.SwiftTargetBuildDescription import struct PackageGraph.ResolvedModule +import struct PackageGraph.ModulesGraph public protocol BuildTarget { var sources: [URL] { get } + /// Whether the target is part of the root package that the user opened or if it's part of a package dependency. + var isPartOfRootPackage: Bool { get } + func compileArguments(for fileURL: URL) throws -> [String] - } +} + +private struct WrappedClangTargetBuildDescription: BuildTarget { + private let description: ClangTargetBuildDescription + let isPartOfRootPackage: Bool + + init(description: ClangTargetBuildDescription, isPartOfRootPackage: Bool) { + self.description = description + self.isPartOfRootPackage = isPartOfRootPackage + } -extension ClangTargetBuildDescription: BuildTarget { public var sources: [URL] { - return (try? compilePaths().map { URL(fileURLWithPath: $0.source.pathString) }) ?? [] + return (try? description.compilePaths().map { URL(fileURLWithPath: $0.source.pathString) }) ?? [] } public func compileArguments(for fileURL: URL) throws -> [String] { let filePath = try resolveSymlinks(try AbsolutePath(validating: fileURL.path)) - let commandLine = try self.emitCommandLine(for: filePath) + let commandLine = try description.emitCommandLine(for: filePath) // First element on the command line is the compiler itself, not an argument. return Array(commandLine.dropFirst()) } @@ -44,9 +56,11 @@ extension ClangTargetBuildDescription: BuildTarget { private struct WrappedSwiftTargetBuildDescription: BuildTarget { private let description: SwiftTargetBuildDescription + let isPartOfRootPackage: Bool - init(description: SwiftTargetBuildDescription) { + init(description: SwiftTargetBuildDescription, isPartOfRootPackage: Bool) { self.description = description + self.isPartOfRootPackage = isPartOfRootPackage } var sources: [URL] { @@ -71,17 +85,27 @@ public struct BuildDescription { } // FIXME: should not use `ResolvedTarget` in the public interface - public func getBuildTarget(for target: ResolvedModule) -> BuildTarget? { + public func getBuildTarget(for target: ResolvedModule, in modulesGraph: ModulesGraph) -> BuildTarget? { if let description = buildPlan.targetMap[target.id] { switch description { case .clang(let description): - return description + return WrappedClangTargetBuildDescription( + description: description, + isPartOfRootPackage: modulesGraph.rootPackages.map(\.id).contains(description.package.id) + ) case .swift(let description): - return WrappedSwiftTargetBuildDescription(description: description) + return WrappedSwiftTargetBuildDescription( + description: description, + isPartOfRootPackage: modulesGraph.rootPackages.map(\.id).contains(description.package.id) + ) } } else { if target.type == .plugin, let package = self.buildPlan.graph.package(for: target) { - return PluginTargetBuildDescription(target: target, toolsVersion: package.manifest.toolsVersion) + return PluginTargetBuildDescription( + target: target, + toolsVersion: package.manifest.toolsVersion, + isPartOfRootPackage: modulesGraph.rootPackages.map(\.id).contains(package.id) + ) } return nil } diff --git a/Sources/SourceKitLSPAPI/PluginTargetBuildDescription.swift b/Sources/SourceKitLSPAPI/PluginTargetBuildDescription.swift index 9fc7403baba..8e6a147cc3e 100644 --- a/Sources/SourceKitLSPAPI/PluginTargetBuildDescription.swift +++ b/Sources/SourceKitLSPAPI/PluginTargetBuildDescription.swift @@ -21,11 +21,13 @@ private import class PackageModel.UserToolchain struct PluginTargetBuildDescription: BuildTarget { private let target: ResolvedModule private let toolsVersion: ToolsVersion + let isPartOfRootPackage: Bool - init(target: ResolvedModule, toolsVersion: ToolsVersion) { + init(target: ResolvedModule, toolsVersion: ToolsVersion, isPartOfRootPackage: Bool) { assert(target.type == .plugin) self.target = target self.toolsVersion = toolsVersion + self.isPartOfRootPackage = isPartOfRootPackage } var sources: [URL] { diff --git a/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift b/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift index 0ef54fbc308..92b275bab08 100644 --- a/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift +++ b/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift @@ -63,7 +63,8 @@ class SourceKitLSPAPITests: XCTestCase { "-emit-dependencies", "-emit-module", "-emit-module-path", "/path/to/build/\(buildParameters.triple)/debug/exe.build/exe.swiftmodule" - ] + ], + isPartOfRootPackage: true ) try description.checkArguments( for: "lib", @@ -73,7 +74,8 @@ class SourceKitLSPAPITests: XCTestCase { "-emit-dependencies", "-emit-module", "-emit-module-path", "/path/to/build/\(buildParameters.triple)/debug/Modules/lib.swiftmodule" - ] + ], + isPartOfRootPackage: true ) } } @@ -82,10 +84,11 @@ extension SourceKitLSPAPI.BuildDescription { @discardableResult func checkArguments( for targetName: String, graph: ModulesGraph, - partialArguments: [String] + partialArguments: [String], + isPartOfRootPackage: Bool ) throws -> Bool { let target = try XCTUnwrap(graph.allTargets.first(where: { $0.name == targetName })) - let buildTarget = try XCTUnwrap(self.getBuildTarget(for: target)) + let buildTarget = try XCTUnwrap(self.getBuildTarget(for: target, in: graph)) guard let file = buildTarget.sources.first else { XCTFail("build target \(targetName) contains no files") @@ -96,6 +99,7 @@ extension SourceKitLSPAPI.BuildDescription { let result = arguments.contains(partialArguments) XCTAssertTrue(result, "could not match \(partialArguments) to actual arguments \(arguments)") + XCTAssertEqual(buildTarget.isPartOfRootPackage, isPartOfRootPackage) return result } } From 35d3de2b209778ce358956a6825569c5890c1235 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 2 May 2024 13:11:46 -0700 Subject: [PATCH 131/159] [ModulesGraph] Fix association between `.tools` targets and their packages While inserting updated `.tools` targets to `modulesToPackages` the logic should use the package where the target resides instead of the package where it's referenced. Resolves: rdar://127369576 --- Sources/PackageGraph/ModulesGraph.swift | 3 ++- .../CrossCompilationPackageGraphTests.swift | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Sources/PackageGraph/ModulesGraph.swift b/Sources/PackageGraph/ModulesGraph.swift index e37b0ab4d54..54db2aa9c59 100644 --- a/Sources/PackageGraph/ModulesGraph.swift +++ b/Sources/PackageGraph/ModulesGraph.swift @@ -207,7 +207,8 @@ public struct ModulesGraph { switch dependency { case .target(let targetDependency, _): allTargets.insert(targetDependency) - modulesToPackages[targetDependency.id] = package + modulesToPackages[targetDependency.id] = + identitiesToPackages[targetDependency.packageIdentity] case .product(let productDependency, _): allProducts.insert(productDependency) productsToPackages[productDependency.id] = diff --git a/Tests/PackageGraphTests/CrossCompilationPackageGraphTests.swift b/Tests/PackageGraphTests/CrossCompilationPackageGraphTests.swift index 69c19852f59..fe5d0541bb5 100644 --- a/Tests/PackageGraphTests/CrossCompilationPackageGraphTests.swift +++ b/Tests/PackageGraphTests/CrossCompilationPackageGraphTests.swift @@ -150,6 +150,20 @@ final class CrossCompilationPackageGraphTests: XCTestCase { XCTAssertEqual(results.filter({ $0.target.buildTriple == .tools }).count, 1) XCTAssertEqual(results.filter({ $0.target.buildTriple == .destination }).count, 1) + + for result in results { + XCTAssertEqual(result.target.packageIdentity, .plain("swift-syntax")) + XCTAssertEqual(graph.package(for: result.target)?.identity, .plain("swift-syntax")) + } + } + + result.checkTargets("SwiftCompilerPlugin") { results in + XCTAssertEqual(results.count, 2) + + for result in results { + XCTAssertEqual(result.target.packageIdentity, .plain("swift-syntax")) + XCTAssertEqual(graph.package(for: result.target)?.identity, .plain("swift-syntax")) + } } } } From 5abdf52eb5c7dcc3a998341beda1e23d7323cc05 Mon Sep 17 00:00:00 2001 From: Ben Barham Date: Thu, 2 May 2024 15:02:46 -0700 Subject: [PATCH 132/159] Remove the addition of external-plugin-path for dev toolchains SDK macro plugins are now provided within `Platforms/.platform/Developer/usr/lib/swift/host/plugins` now, which is always added by the driver. --- .../SwiftTargetBuildDescription.swift | 19 ------------- Sources/PackageModel/Toolchain.swift | 6 ---- Sources/PackageModel/UserToolchain.swift | 28 ------------------- .../SPMTestSupport/MockBuildTestHelper.swift | 2 -- 4 files changed, 55 deletions(-) diff --git a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift index 7c9c6ff0b35..7460c8ce2ef 100644 --- a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift @@ -451,25 +451,6 @@ package final class SwiftTargetBuildDescription { } #endif - // If we're using an OSS toolchain, add the required arguments bringing in the plugin server from the default toolchain if available. - if self.defaultBuildParameters.toolchain.isSwiftDevelopmentToolchain, - DriverSupport.checkSupportedFrontendFlags( - flags: ["-external-plugin-path"], - toolchain: self.defaultBuildParameters.toolchain, - fileSystem: self.fileSystem - ), - let pluginServer = try self.defaultBuildParameters.toolchain.swiftPluginServerPath - { - let toolchainUsrPath = pluginServer.parentDirectory.parentDirectory - let pluginPathComponents = ["lib", "swift", "host", "plugins"] - - let pluginPath = toolchainUsrPath.appending(components: pluginPathComponents) - args += ["-Xfrontend", "-external-plugin-path", "-Xfrontend", "\(pluginPath)#\(pluginServer.pathString)"] - - let localPluginPath = toolchainUsrPath.appending(components: ["local"] + pluginPathComponents) - args += ["-Xfrontend", "-external-plugin-path", "-Xfrontend", "\(localPluginPath)#\(pluginServer.pathString)"] - } - if self.shouldDisableSandbox { let toolchainSupportsDisablingSandbox = DriverSupport.checkSupportedFrontendFlags( flags: ["-disable-sandbox"], diff --git a/Sources/PackageModel/Toolchain.swift b/Sources/PackageModel/Toolchain.swift index 8009f62d326..48764732c91 100644 --- a/Sources/PackageModel/Toolchain.swift +++ b/Sources/PackageModel/Toolchain.swift @@ -25,12 +25,6 @@ public protocol Toolchain { /// Path to `lib/swift_static` var swiftStaticResourcesPath: AbsolutePath? { get } - /// Whether the used compiler is from a open source development toolchain. - var isSwiftDevelopmentToolchain: Bool { get } - - /// Path to the Swift plugin server utility. - var swiftPluginServerPath: AbsolutePath? { get throws } - /// Path containing the macOS Swift stdlib. var macosSwiftStdlib: AbsolutePath { get throws } diff --git a/Sources/PackageModel/UserToolchain.swift b/Sources/PackageModel/UserToolchain.swift index 7d1f833e903..eedce88bd60 100644 --- a/Sources/PackageModel/UserToolchain.swift +++ b/Sources/PackageModel/UserToolchain.swift @@ -84,8 +84,6 @@ public final class UserToolchain: Toolchain { private let environment: EnvironmentVariables - public let isSwiftDevelopmentToolchain: Bool - public let installedSwiftPMConfiguration: InstalledSwiftPMConfiguration public let providedLibraries: [ProvidedLibrary] @@ -514,22 +512,6 @@ public final class UserToolchain: Toolchain { self.swiftCompilerPath = swiftCompilers.compile self.architectures = swiftSDK.architectures - #if canImport(Darwin) - let toolchainPlistPath = self.swiftCompilerPath.parentDirectory.parentDirectory.parentDirectory - .appending(component: "Info.plist") - if localFileSystem.exists(toolchainPlistPath), let toolchainPlist = try? NSDictionary( - contentsOf: URL(fileURLWithPath: toolchainPlistPath.pathString), - error: () - ), let overrideBuildSettings = toolchainPlist["OverrideBuildSettings"] as? NSDictionary, - let isSwiftDevelopmentToolchainStringValue = overrideBuildSettings["SWIFT_DEVELOPMENT_TOOLCHAIN"] as? String { - self.isSwiftDevelopmentToolchain = isSwiftDevelopmentToolchainStringValue == "YES" - } else { - self.isSwiftDevelopmentToolchain = false - } - #else - self.isSwiftDevelopmentToolchain = false - #endif - if let customInstalledSwiftPMConfiguration { self.installedSwiftPMConfiguration = customInstalledSwiftPMConfiguration } else { @@ -879,16 +861,6 @@ public final class UserToolchain: Toolchain { configuration.xctestPath } - private let _swiftPluginServerPath = ThreadSafeBox() - - public var swiftPluginServerPath: AbsolutePath? { - get throws { - try _swiftPluginServerPath.memoize { - return try Self.derivePluginServerPath(triple: self.targetTriple) - } - } - } - private static func loadJSONResource( config: AbsolutePath, type: T.Type, `default`: T ) diff --git a/Sources/SPMTestSupport/MockBuildTestHelper.swift b/Sources/SPMTestSupport/MockBuildTestHelper.swift index 9ce3ac5fa67..798204d51b1 100644 --- a/Sources/SPMTestSupport/MockBuildTestHelper.swift +++ b/Sources/SPMTestSupport/MockBuildTestHelper.swift @@ -34,9 +34,7 @@ package struct MockToolchain: PackageModel.Toolchain { package let librarySearchPaths = [AbsolutePath]() package let swiftResourcesPath: AbsolutePath? package let swiftStaticResourcesPath: AbsolutePath? = nil - package let isSwiftDevelopmentToolchain = false package let sdkRootPath: AbsolutePath? = nil - package let swiftPluginServerPath: AbsolutePath? = nil package let extraFlags = PackageModel.BuildFlags() package let installedSwiftPMConfiguration = InstalledSwiftPMConfiguration.default package let providedLibraries = [ProvidedLibrary]() From 605e22860e04e3b03b6631b0ab64eb206d59a4da Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Fri, 3 May 2024 04:27:59 -0700 Subject: [PATCH 133/159] package -> @_spi public due to package exportability rule updates (#7525) The exportability rules around `package` access level are updated in a way that an `@_spi public` can't be bound to a `package` var, similar to how `@_spi public` can't be bound to a `public` var. For reference, see https://github.com/apple/swift/pull/73161. This PR corrects such use case. --- Sources/PackageModel/SwiftSDKs/SwiftSDK.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift b/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift index cb929a88350..2f979c8b26d 100644 --- a/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift +++ b/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift @@ -170,7 +170,8 @@ public struct SwiftSDK: Equatable { } /// Whether or not the receiver supports using XCTest. - package let xctestSupport: XCTestSupport + @_spi(SwiftPMInternal) + public let xctestSupport: XCTestSupport /// Root directory path of the SDK used to compile for the target triple. @available(*, deprecated, message: "use `pathsConfiguration.sdkRootPath` instead") From 32442c6604bebdeac45e3433aa7690967bb1170c Mon Sep 17 00:00:00 2001 From: coffmark <52638834+coffmark@users.noreply.github.com> Date: Fri, 3 May 2024 22:19:13 +0900 Subject: [PATCH 134/159] Add `Sendable` annotations to SwiftVersion (#7527) I fixed warning appears when building with Swift 6. --- Sources/Basics/SwiftVersion.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Basics/SwiftVersion.swift b/Sources/Basics/SwiftVersion.swift index d3967783532..7d95a327ca4 100644 --- a/Sources/Basics/SwiftVersion.swift +++ b/Sources/Basics/SwiftVersion.swift @@ -16,7 +16,7 @@ import TSCclibc #endif -public struct SwiftVersion { +public struct SwiftVersion: Sendable { /// The version number. public var version: (major: Int, minor: Int, patch: Int) From cca73b254e8e77c77bfb7fd28317debefbaf79b9 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Wed, 8 May 2024 01:38:27 +0100 Subject: [PATCH 135/159] `swift sdk`: print warnings on stderr instead of stdout (#7532) Tools and scripts may rely on command output to remain parseable, so printing these warnings on `stdout` can break such workflows. We should output these warnings on `stderr` instead. As these warnings are emitted before any logging is available, we're using `fputs` for output on `stderr` --- .../swift-experimental-sdk/Entrypoint.swift | 3 ++- Sources/swift-package-manager/SwiftPM.swift | 4 ++-- Tests/CommandsTests/SDKCommandTests.swift | 19 +++++++++---------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Sources/swift-experimental-sdk/Entrypoint.swift b/Sources/swift-experimental-sdk/Entrypoint.swift index f908ff3ff10..7e9aa3cc642 100644 --- a/Sources/swift-experimental-sdk/Entrypoint.swift +++ b/Sources/swift-experimental-sdk/Entrypoint.swift @@ -11,11 +11,12 @@ //===----------------------------------------------------------------------===// import SwiftSDKCommand +import Foundation @main struct Entrypoint { static func main() async { - print("warning: `swift experimental-sdk` command is deprecated and will be removed in a future version of SwiftPM. Use `swift sdk` instead.") + fputs("warning: `swift experimental-sdk` command is deprecated and will be removed in a future version of SwiftPM. Use `swift sdk` instead.", stderr) await SwiftSDKCommand.main() } } diff --git a/Sources/swift-package-manager/SwiftPM.swift b/Sources/swift-package-manager/SwiftPM.swift index fa6fc156023..5440b72b79f 100644 --- a/Sources/swift-package-manager/SwiftPM.swift +++ b/Sources/swift-package-manager/SwiftPM.swift @@ -13,7 +13,7 @@ import Basics import Commands - +import Foundation import SwiftSDKCommand import PackageCollectionsCommand import PackageRegistryCommand @@ -42,7 +42,7 @@ struct SwiftPM { case "swift-build": await SwiftBuildCommand.main() case "swift-experimental-sdk": - print("warning: `swift experimental-sdk` command is deprecated and will be removed in a future version of SwiftPM. Use `swift sdk` instead.") + fputs("warning: `swift experimental-sdk` command is deprecated and will be removed in a future version of SwiftPM. Use `swift sdk` instead.", stderr) fallthrough case "swift-sdk": await SwiftSDKCommand.main() diff --git a/Tests/CommandsTests/SDKCommandTests.swift b/Tests/CommandsTests/SDKCommandTests.swift index 0c74126d8ba..55619494954 100644 --- a/Tests/CommandsTests/SDKCommandTests.swift +++ b/Tests/CommandsTests/SDKCommandTests.swift @@ -48,7 +48,8 @@ final class SDKCommandTests: CommandsTestCase { ) if command == .experimentalSDK { - XCTAssertMatch(stdout, .contains(deprecationWarning)) + XCTAssertMatch(stderr, .contains(deprecationWarning)) + XCTAssertNoMatch(stdout, .contains(deprecationWarning)) } // We only expect tool's output on the stdout stream. @@ -57,18 +58,16 @@ final class SDKCommandTests: CommandsTestCase { .contains("\(bundle)` successfully installed as test-sdk.artifactbundle.") ) - XCTAssertEqual(stderr.count, 0) - (stdout, stderr) = try command.execute( ["list", "--swift-sdks-path", fixturePath.pathString]) if command == .experimentalSDK { - XCTAssertMatch(stdout, .contains(deprecationWarning)) + XCTAssertMatch(stderr, .contains(deprecationWarning)) + XCTAssertNoMatch(stdout, .contains(deprecationWarning)) } // We only expect tool's output on the stdout stream. XCTAssertMatch(stdout, .contains("test-artifact")) - XCTAssertEqual(stderr.count, 0) XCTAssertThrowsError(try command.execute( [ @@ -91,30 +90,30 @@ final class SDKCommandTests: CommandsTestCase { } if command == .experimentalSDK { - XCTAssertMatch(stdout, .contains(deprecationWarning)) + XCTAssertMatch(stderr, .contains(deprecationWarning)) } (stdout, stderr) = try command.execute( ["remove", "--swift-sdks-path", fixturePath.pathString, "test-artifact"]) if command == .experimentalSDK { - XCTAssertMatch(stdout, .contains(deprecationWarning)) + XCTAssertMatch(stderr, .contains(deprecationWarning)) + XCTAssertNoMatch(stdout, .contains(deprecationWarning)) } // We only expect tool's output on the stdout stream. XCTAssertMatch(stdout, .contains("test-sdk.artifactbundle` was successfully removed from the file system.")) - XCTAssertEqual(stderr.count, 0) (stdout, stderr) = try command.execute( ["list", "--swift-sdks-path", fixturePath.pathString]) if command == .experimentalSDK { - XCTAssertMatch(stdout, .contains(deprecationWarning)) + XCTAssertMatch(stderr, .contains(deprecationWarning)) + XCTAssertNoMatch(stdout, .contains(deprecationWarning)) } // We only expect tool's output on the stdout stream. XCTAssertNoMatch(stdout, .contains("test-artifact")) - XCTAssertEqual(stderr.count, 0) } } } From a37631a1cb1d449a04f07ddd9d2cd41666ce771f Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 8 May 2024 01:32:02 -0700 Subject: [PATCH 136/159] Teach swift package add-target --type test about swift-testing (#7481) Introduce a command-line argument `--testing-library` to the add-target command to specify which test library to generate the test for. This can be 'xctest' (the prior XCTest behavior), 'swift-testing' (to use the new swift-testing library), or 'none' (for no test library at all). For the new swift-testing generation, also add the appropriate package and test target dependency, along with a stub testsuite to start from. Fixes https://github.com/apple/swift-package-manager/issues/7478 --- .../Commands/PackageCommands/AddTarget.swift | 6 + .../InstalledSwiftPMConfiguration.swift | 54 ++++++- Sources/PackageModelSyntax/AddTarget.swift | 134 ++++++++++++++++-- .../ManifestEditTests.swift | 47 ++++++ Utilities/config.json | 4 +- 5 files changed, 232 insertions(+), 13 deletions(-) diff --git a/Sources/Commands/PackageCommands/AddTarget.swift b/Sources/Commands/PackageCommands/AddTarget.swift index 0bd46584f57..5f3f30ad806 100644 --- a/Sources/Commands/PackageCommands/AddTarget.swift +++ b/Sources/Commands/PackageCommands/AddTarget.swift @@ -21,6 +21,8 @@ import TSCBasic import TSCUtility import Workspace +extension AddTarget.TestHarness: ExpressibleByArgument { } + extension SwiftPackageCommand { struct AddTarget: SwiftCommand { /// The type of target that can be specified on the command line. @@ -58,6 +60,9 @@ extension SwiftPackageCommand { @Option(help: "The checksum for a remote binary target") var checksum: String? + @Option(help: "The testing library to use when generating test targets, which can be one of 'xctest', 'swift-testing', or 'none'") + var testingLibrary: PackageModelSyntax.AddTarget.TestHarness = .default + func run(_ swiftCommandState: SwiftCommandState) throws { let workspace = try swiftCommandState.getActiveWorkspace() @@ -110,6 +115,7 @@ extension SwiftPackageCommand { let editResult = try PackageModelSyntax.AddTarget.addTarget( target, to: manifestSyntax, + configuration: .init(testHarness: testingLibrary), installedSwiftPMConfiguration: swiftCommandState .getHostToolchain() .installedSwiftPMConfiguration diff --git a/Sources/PackageModel/InstalledSwiftPMConfiguration.swift b/Sources/PackageModel/InstalledSwiftPMConfiguration.swift index a34a3d22dad..00c637289d6 100644 --- a/Sources/PackageModel/InstalledSwiftPMConfiguration.swift +++ b/Sources/PackageModel/InstalledSwiftPMConfiguration.swift @@ -10,7 +10,7 @@ // //===----------------------------------------------------------------------===// -public struct InstalledSwiftPMConfiguration: Codable { +public struct InstalledSwiftPMConfiguration { public struct Version: Codable, CustomStringConvertible { let major: Int let minor: Int @@ -31,6 +31,7 @@ public struct InstalledSwiftPMConfiguration: Codable { let version: Int public let swiftSyntaxVersionForMacroTemplate: Version + public let swiftTestingVersionForTestTemplate: Version public static var `default`: InstalledSwiftPMConfiguration { return .init( @@ -40,7 +41,56 @@ public struct InstalledSwiftPMConfiguration: Codable { minor: 0, patch: 0, prereleaseIdentifier: "latest" - ) + ), + swiftTestingVersionForTestTemplate: defaultSwiftTestingVersionForTestTemplate ) } + + private static var defaultSwiftTestingVersionForTestTemplate: Version { + .init( + major: 0, + minor: 8, + patch: 0, + prereleaseIdentifier: nil + ) + } +} + +extension InstalledSwiftPMConfiguration: Codable { + enum CodingKeys: CodingKey { + case version + case swiftSyntaxVersionForMacroTemplate + case swiftTestingVersionForTestTemplate + } + + public init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + self.version = try container.decode( + Int.self, + forKey: CodingKeys.version + ) + self.swiftSyntaxVersionForMacroTemplate = try container.decode( + Version.self, + forKey: CodingKeys.swiftSyntaxVersionForMacroTemplate + ) + self.swiftTestingVersionForTestTemplate = try container.decodeIfPresent( + Version.self, + forKey: CodingKeys.swiftTestingVersionForTestTemplate + ) ?? InstalledSwiftPMConfiguration.defaultSwiftTestingVersionForTestTemplate + } + + public func encode(to encoder: any Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + try container.encode(self.version, forKey: CodingKeys.version) + try container.encode( + self.swiftSyntaxVersionForMacroTemplate, + forKey: CodingKeys.swiftSyntaxVersionForMacroTemplate + ) + try container.encode( + self.swiftTestingVersionForTestTemplate, + forKey: CodingKeys.swiftTestingVersionForTestTemplate + ) + } } diff --git a/Sources/PackageModelSyntax/AddTarget.swift b/Sources/PackageModelSyntax/AddTarget.swift index 7fda6f58229..75bdbad37b5 100644 --- a/Sources/PackageModelSyntax/AddTarget.swift +++ b/Sources/PackageModelSyntax/AddTarget.swift @@ -30,12 +30,40 @@ public struct AddTarget { "cxxLanguageStandard" ] + /// The kind of test harness to use. This isn't part of the manifest + /// itself, but is used to guide the generation process. + public enum TestHarness: String, Codable { + /// Don't use any library + case none + + /// Create a test using the XCTest library. + case xctest + + /// Create a test using the swift-testing package. + case swiftTesting = "swift-testing" + + /// The default testing library to use. + public static var `default`: TestHarness = .xctest + } + + /// Additional configuration information to guide the package editing + /// process. + public struct Configuration { + /// The test harness to use. + public var testHarness: TestHarness + + public init(testHarness: TestHarness = .default) { + self.testHarness = testHarness + } + } + /// Add the given target to the manifest, producing a set of edit results /// that updates the manifest and adds some source files to stub out the /// new target. public static func addTarget( _ target: TargetDescription, to manifest: SourceFileSyntax, + configuration: Configuration = .init(), installedSwiftPMConfiguration: InstalledSwiftPMConfiguration = .default ) throws -> PackageEditResult { // Make sure we have a suitable tools version in the manifest. @@ -49,10 +77,20 @@ public struct AddTarget { // content when needed. var target = target - // Macro targets need to depend on a couple of libraries from - // SwiftSyntax. - if target.type == .macro { + // Add dependencies needed for various targets. + switch target.type { + case .macro: + // Macro targets need to depend on a couple of libraries from + // SwiftSyntax. target.dependencies.append(contentsOf: macroTargetDependencies) + + case .test where configuration.testHarness == .swiftTesting: + // Testing targets using swift-testing need to depend on + // SwiftTesting from the swift-testing package. + target.dependencies.append(contentsOf: swiftTestingTestTargetDependencies) + + default: + break; } var newPackageCall = try packageCall.appendingToArrayArgument( @@ -84,6 +122,7 @@ public struct AddTarget { addPrimarySourceFile( outerPath: outerPath, target: target, + configuration: configuration, to: &auxiliaryFiles ) @@ -124,6 +163,17 @@ public struct AddTarget { } } + case .test where configuration.testHarness == .swiftTesting: + if !manifest.description.contains("swift-testing") { + newPackageCall = try AddPackageDependency + .addPackageDependencyLocal( + .swiftTesting( + configuration: installedSwiftPMConfiguration + ), + to: newPackageCall + ) + } + default: break; } @@ -140,6 +190,7 @@ public struct AddTarget { fileprivate static func addPrimarySourceFile( outerPath: RelativePath, target: TargetDescription, + configuration: Configuration, to auxiliaryFiles: inout AuxiliaryFiles ) { let sourceFilePath = outerPath.appending( @@ -153,7 +204,17 @@ public struct AddTarget { // Add appropriate test module dependencies. if target.type == .test { - importModuleNames.append("XCTest") + switch configuration.testHarness { + case .none: + break + + case .xctest: + importModuleNames.append("XCTest") + + case .swiftTesting: + // Import is handled by the added dependency. + break + } } let importDecls = importModuleNames.lazy.sorted().map { name in @@ -184,14 +245,35 @@ public struct AddTarget { """ case .test: - """ - \(imports) - class \(raw: target.name): XCTestCase { - func test\(raw: target.name)() { - XCTAssertEqual(42, 17 + 25) + switch configuration.testHarness { + case .none: + """ + \(imports) + // Test code here + """ + + case .xctest: + """ + \(imports) + class \(raw: target.name): XCTestCase { + func test\(raw: target.name)() { + XCTAssertEqual(42, 17 + 25) + } + } + """ + + case .swiftTesting: + """ + \(imports) + @Suite + struct \(raw: target.name)Tests { + @Test("\(raw: target.name) tests") + func example() { + #expect(42 == 17 + 25) + } } + """ } - """ case .regular: """ @@ -298,3 +380,35 @@ fileprivate extension PackageDependency { ) } } + +/// The set of dependencies we need to introduce to a newly-created macro +/// target. +fileprivate let swiftTestingTestTargetDependencies: [TargetDescription.Dependency] = [ + .product(name: "Testing", package: "swift-testing"), +] + + +/// The package dependency for swift-testing, for use in test files. +fileprivate extension PackageDependency { + /// Source control URL for the swift-syntax package. + static var swiftTestingURL: SourceControlURL { + "https://github.com/apple/swift-testing.git" + } + + /// Package dependency on the swift-testing package. + static func swiftTesting( + configuration: InstalledSwiftPMConfiguration + ) -> PackageDependency { + let swiftTestingVersionDefault = + configuration.swiftTestingVersionForTestTemplate + let swiftTestingVersion = Version(swiftTestingVersionDefault.description)! + + return .sourceControl( + identity: PackageIdentity(url: swiftTestingURL), + nameForTargetDependencyResolutionOnly: nil, + location: .remote(swiftTestingURL), + requirement: .range(.upToNextMajor(from: swiftTestingVersion)), + productFilter: .everything + ) + } +} diff --git a/Tests/PackageModelSyntaxTests/ManifestEditTests.swift b/Tests/PackageModelSyntaxTests/ManifestEditTests.swift index 2acb6debd62..812b72bfe84 100644 --- a/Tests/PackageModelSyntaxTests/ManifestEditTests.swift +++ b/Tests/PackageModelSyntaxTests/ManifestEditTests.swift @@ -564,6 +564,53 @@ class ManifestEditTests: XCTestCase { } } + func testAddSwiftTestingTestTarget() throws { + try assertManifestRefactor(""" + // swift-tools-version: 5.5 + let package = Package( + name: "packages" + ) + """, + expectedManifest: """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: [ + .package(url: "https://github.com/apple/swift-testing.git", from: "0.8.0"), + ], + targets: [ + .testTarget( + name: "MyTest", + dependencies: [ .product(name: "Testing", package: "swift-testing") ] + ), + ] + ) + """, + expectedAuxiliarySources: [ + RelativePath("Tests/MyTest/MyTest.swift") : """ + import Testing + + @Suite + struct MyTestTests { + @Test("MyTest tests") + func example() { + #expect(42 == 17 + 25) + } + } + """ + ]) { manifest in + try AddTarget.addTarget( + TargetDescription( + name: "MyTest", + type: .test + ), + to: manifest, + configuration: .init( + testHarness: .swiftTesting + ) + ) + } + } } diff --git a/Utilities/config.json b/Utilities/config.json index dc8ec6cafe9..b31399a6276 100644 --- a/Utilities/config.json +++ b/Utilities/config.json @@ -1 +1,3 @@ -{"version":1,"swiftSyntaxVersionForMacroTemplate":{"major":600,"minor":0,"patch":0, "prereleaseIdentifier":"latest"}} +{"version":1, + "swiftSyntaxVersionForMacroTemplate":{"major":600,"minor":0,"patch":0, "prereleaseIdentifier":"latest"}, + "swiftTestingVersionForTestTemplate":{"major":0,"minor":8,"patch":0}} From 8ca3091df84588efca789f706374dbc286900a95 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Wed, 8 May 2024 13:23:37 -0400 Subject: [PATCH 137/159] Pass through swift-testing's experimental JSON streams from `swift test` (#7534) This PR adds two undocumented/unsupported experimental options to `swift test` to allow passing through JSON files/streams for input and output. These streams are still under development in swift-testing, so these options are not yet supported, but this change will allow interested coders to experiment with them. --------- Co-authored-by: Max Desiatov --- Sources/Commands/SwiftTestCommand.swift | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Sources/Commands/SwiftTestCommand.swift b/Sources/Commands/SwiftTestCommand.swift index a4d09edd0cd..d0aae87ce7e 100644 --- a/Sources/Commands/SwiftTestCommand.swift +++ b/Sources/Commands/SwiftTestCommand.swift @@ -154,6 +154,16 @@ struct TestCommandOptions: ParsableArguments { var enableExperimentalTestOutput: Bool { return testOutput == .experimentalSummary } + + /// Path where swift-testing's JSON configuration should be read. + @Option(name: .customLong("experimental-configuration-path"), + help: .hidden) + var configurationPath: AbsolutePath? + + /// Path where swift-testing's JSON output should be written. + @Option(name: .customLong("experimental-event-stream-output"), + help: .hidden) + var eventStreamOutputPath: AbsolutePath? } /// Tests filtering specifier, which is used to filter tests to run. @@ -688,9 +698,10 @@ extension SwiftTestCommand { sanitizers: globalOptions.build.sanitizers ) + let additionalArguments = ["--list-tests"] + CommandLine.arguments.dropFirst() let runner = TestRunner( bundlePaths: testProducts.map(\.binaryPath), - additionalArguments: ["--list-tests"], + additionalArguments: additionalArguments, cancellator: swiftCommandState.cancellator, toolchain: toolchain, testEnv: testEnv, From 8b129093e02bb30c2dd9d3af5187435aba25f53e Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 8 May 2024 11:53:04 -0700 Subject: [PATCH 138/159] [Package/ModuleGraph] Allow cyclic package dependencies if they don't introduce a cycle in a build graph (#7530) ### Motivation: It should be possible for packages to depend on each other if such dependence doesn't introduce cycles in the build graph. ### Modifications: - Introduced a new DFS method to walk over graphs that breaks cycles. - Replaces use of `findCycle` + `topologicalSort` with `DFS` while building manifest and package graphs. This allows cycles in dependencies to be modeled correctly. - Removes some of the redundant data transformations from modules graph. - Modifies `ResolvedPackage` to carry identities of its dependencies instead of resolved dependencies themselves. This helps to simplify logic in `createResolvedPackages`. - Adds detection of target cycles across packages. ### Result: Makes it possible for package A to depend on package B and B to depend on A if their targets don't form a cycle. --- CHANGELOG.md | 6 + Sources/Basics/CMakeLists.txt | 1 + Sources/Basics/Graph/GraphAlgorithms.swift | 57 +++++++ .../PackageCommands/CompletionCommand.swift | 1 + .../PackageCommands/PluginCommand.swift | 16 +- .../PackageCommands/ShowDependencies.swift | 10 +- .../Utilities/DependenciesSerializer.swift | 22 +-- .../PackageGraph/ModulesGraph+Loading.swift | 156 +++++++----------- Sources/PackageGraph/ModulesGraph.swift | 58 ++----- .../Resolution/ResolvedPackage.swift | 4 +- .../Plugins/PluginContextSerializer.swift | 3 +- .../Plugins/PluginInvocation.swift | 5 + .../SPMTestSupport/PackageGraphTester.swift | 2 +- Sources/Workspace/Workspace+Manifests.swift | 52 +++--- Sources/Workspace/Workspace+Signing.swift | 4 +- Sources/Workspace/Workspace.swift | 2 +- .../MermaidPackageSerializerTests.swift | 4 +- Tests/CommandsTests/PackageCommandTests.swift | 1 + Tests/FunctionalTests/PluginTests.swift | 2 + .../PackageGraphTests/ModulesGraphTests.swift | 63 ++++++- Tests/WorkspaceTests/WorkspaceTests.swift | 24 ++- 21 files changed, 275 insertions(+), 218 deletions(-) create mode 100644 Sources/Basics/Graph/GraphAlgorithms.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 35fa173eaba..26faec224b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ Note: This is in reverse chronological order, so newer entries are added to the top. +* [#7530] + + Makes it possible for packages to depend on each other if such dependency doesn't form any target-level cycles. For example, + package `A` can depend on `B` and `B` on `A` unless targets in `B` depend on products of `A` that depend on some of the same + targets from `B` and vice versa. + Swift 6.0 ----------- diff --git a/Sources/Basics/CMakeLists.txt b/Sources/Basics/CMakeLists.txt index bb3948ef432..343bbedbc56 100644 --- a/Sources/Basics/CMakeLists.txt +++ b/Sources/Basics/CMakeLists.txt @@ -36,6 +36,7 @@ add_library(Basics FileSystem/VFSOverlay.swift Graph/AdjacencyMatrix.swift Graph/DirectedGraph.swift + Graph/GraphAlgorithms.swift Graph/UndirectedGraph.swift SourceControlURL.swift HTTPClient/HTTPClient.swift diff --git a/Sources/Basics/Graph/GraphAlgorithms.swift b/Sources/Basics/Graph/GraphAlgorithms.swift new file mode 100644 index 00000000000..6f7d98970a8 --- /dev/null +++ b/Sources/Basics/Graph/GraphAlgorithms.swift @@ -0,0 +1,57 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2015-2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import struct OrderedCollections.OrderedSet + +/// Implements a pre-order depth-first search. +/// +/// The cycles are handled by skipping cycle points but it should be possible to +/// to extend this in the future to provide a callback for every cycle. +/// +/// - Parameters: +/// - nodes: The list of input nodes to sort. +/// - successors: A closure for fetching the successors of a particular node. +/// - onUnique: A callback to indicate the the given node is being processed for the first time. +/// - onDuplicate: A callback to indicate that the node was already processed at least once. +/// +/// - Complexity: O(v + e) where (v, e) are the number of vertices and edges +/// reachable from the input nodes via the relation. +public func depthFirstSearch( + _ nodes: [T], + successors: (T) throws -> [T], + onUnique: (T) -> Void, + onDuplicate: (T, T) -> Void +) rethrows { + var stack = OrderedSet() + var visited = Set() + + for node in nodes { + precondition(stack.isEmpty) + stack.append(node) + + while !stack.isEmpty { + let curr = stack.removeLast() + + let visitResult = visited.insert(curr) + if visitResult.inserted { + onUnique(curr) + } else { + onDuplicate(visitResult.memberAfterInsert, curr) + continue + } + + for succ in try successors(curr) { + stack.append(succ) + } + } + } +} diff --git a/Sources/Commands/PackageCommands/CompletionCommand.swift b/Sources/Commands/PackageCommands/CompletionCommand.swift index 021ad4fa9fe..18fd0e50f6a 100644 --- a/Sources/Commands/PackageCommands/CompletionCommand.swift +++ b/Sources/Commands/PackageCommands/CompletionCommand.swift @@ -68,6 +68,7 @@ extension SwiftPackageCommand { // command's result output goes on stdout // ie "swift package list-dependencies" should output to stdout ShowDependencies.dumpDependenciesOf( + graph: graph, rootPackage: graph.rootPackages[graph.rootPackages.startIndex], mode: .flatlist, on: TSCBasic.stdoutStream diff --git a/Sources/Commands/PackageCommands/PluginCommand.swift b/Sources/Commands/PackageCommands/PluginCommand.swift index 3255e1d854b..5c8b6f6a413 100644 --- a/Sources/Commands/PackageCommands/PluginCommand.swift +++ b/Sources/Commands/PackageCommands/PluginCommand.swift @@ -371,6 +371,7 @@ struct PluginCommand: SwiftCommand { pkgConfigDirectories: swiftCommandState.options.locations.pkgConfigDirectories, sdkRootPath: buildParameters.toolchain.sdkRootPath, fileSystem: swiftCommandState.fileSystem, + modulesGraph: packageGraph, observabilityScope: swiftCommandState.observabilityScope, callbackQueue: delegateQueue, delegate: pluginDelegate, @@ -382,10 +383,17 @@ struct PluginCommand: SwiftCommand { static func availableCommandPlugins(in graph: ModulesGraph, limitedTo packageIdentity: String?) -> [PluginTarget] { // All targets from plugin products of direct dependencies are "available". - let directDependencyPackages = graph.rootPackages.flatMap { $0.dependencies }.filter { $0.matching(identity: packageIdentity) } + let directDependencyPackages = graph.rootPackages.flatMap { + $0.dependencies + }.filter { + $0.matching(identity: packageIdentity) + }.compactMap { + graph.package(for: $0) + } + let directDependencyPluginTargets = directDependencyPackages.flatMap { $0.products.filter { $0.type == .plugin } }.flatMap { $0.targets } // As well as any plugin targets in root packages. - let rootPackageTargets = graph.rootPackages.filter { $0.matching(identity: packageIdentity) }.flatMap { $0.targets } + let rootPackageTargets = graph.rootPackages.filter { $0.identity.matching(identity: packageIdentity) }.flatMap { $0.targets } return (directDependencyPluginTargets + rootPackageTargets).compactMap { $0.underlying as? PluginTarget }.filter { switch $0.capability { case .buildTool: return false @@ -469,10 +477,10 @@ extension SandboxNetworkPermission { } } -extension ResolvedPackage { +extension PackageIdentity { fileprivate func matching(identity: String?) -> Bool { if let identity { - return self.identity == .plain(identity) + return self == .plain(identity) } else { return true } diff --git a/Sources/Commands/PackageCommands/ShowDependencies.swift b/Sources/Commands/PackageCommands/ShowDependencies.swift index bd65237bb02..4c33d937181 100644 --- a/Sources/Commands/PackageCommands/ShowDependencies.swift +++ b/Sources/Commands/PackageCommands/ShowDependencies.swift @@ -43,13 +43,19 @@ extension SwiftPackageCommand { // ie "swift package show-dependencies" should output to stdout let stream: OutputByteStream = try outputPath.map { try LocalFileOutputByteStream($0) } ?? TSCBasic.stdoutStream Self.dumpDependenciesOf( + graph: graph, rootPackage: graph.rootPackages[graph.rootPackages.startIndex], mode: format, on: stream ) } - static func dumpDependenciesOf(rootPackage: ResolvedPackage, mode: ShowDependenciesMode, on stream: OutputByteStream) { + static func dumpDependenciesOf( + graph: ModulesGraph, + rootPackage: ResolvedPackage, + mode: ShowDependenciesMode, + on stream: OutputByteStream + ) { let dumper: DependenciesDumper switch mode { case .text: @@ -61,7 +67,7 @@ extension SwiftPackageCommand { case .flatlist: dumper = FlatListDumper() } - dumper.dump(dependenciesOf: rootPackage, on: stream) + dumper.dump(graph: graph, dependenciesOf: rootPackage, on: stream) stream.flush() } diff --git a/Sources/Commands/Utilities/DependenciesSerializer.swift b/Sources/Commands/Utilities/DependenciesSerializer.swift index 3a036bec15f..25190f011c4 100644 --- a/Sources/Commands/Utilities/DependenciesSerializer.swift +++ b/Sources/Commands/Utilities/DependenciesSerializer.swift @@ -17,11 +17,11 @@ import enum TSCBasic.JSON import protocol TSCBasic.OutputByteStream protocol DependenciesDumper { - func dump(dependenciesOf: ResolvedPackage, on: OutputByteStream) + func dump(graph: ModulesGraph, dependenciesOf: ResolvedPackage, on: OutputByteStream) } final class PlainTextDumper: DependenciesDumper { - func dump(dependenciesOf rootpkg: ResolvedPackage, on stream: OutputByteStream) { + func dump(graph: ModulesGraph, dependenciesOf rootpkg: ResolvedPackage, on stream: OutputByteStream) { func recursiveWalk(packages: [ResolvedPackage], prefix: String = "") { var hanger = prefix + "├── " @@ -39,14 +39,14 @@ final class PlainTextDumper: DependenciesDumper { var childPrefix = hanger let startIndex = childPrefix.index(childPrefix.endIndex, offsetBy: -4) childPrefix.replaceSubrange(startIndex.. = [] func printNode(_ package: ResolvedPackage) { let url = package.manifest.packageLocation @@ -87,7 +87,7 @@ final class DotDumper: DependenciesDumper { var dependenciesAlreadyPrinted: Set = [] func recursiveWalk(rootpkg: ResolvedPackage) { printNode(rootpkg) - for dependency in rootpkg.dependencies { + for dependency in graph.directDependencies(for: rootpkg) { let rootURL = rootpkg.manifest.packageLocation let dependencyURL = dependency.manifest.packageLocation let urlPair = DependencyURLs(root: rootURL, dependency: dependencyURL) @@ -120,7 +120,7 @@ final class DotDumper: DependenciesDumper { } final class JSONDumper: DependenciesDumper { - func dump(dependenciesOf rootpkg: ResolvedPackage, on stream: OutputByteStream) { + func dump(graph: ModulesGraph, dependenciesOf rootpkg: ResolvedPackage, on stream: OutputByteStream) { func convert(_ package: ResolvedPackage) -> JSON { return .orderedDictionary([ "identity": .string(package.identity.description), @@ -128,7 +128,7 @@ final class JSONDumper: DependenciesDumper { "url": .string(package.manifest.packageLocation), "version": .string(package.manifest.version?.description ?? "unspecified"), "path": .string(package.path.pathString), - "dependencies": .array(package.dependencies.map(convert)), + "dependencies": .array(package.dependencies.compactMap { graph.packages[$0] }.map(convert)), ]) } diff --git a/Sources/PackageGraph/ModulesGraph+Loading.swift b/Sources/PackageGraph/ModulesGraph+Loading.swift index b20ef4c3ee7..e99aef7f964 100644 --- a/Sources/PackageGraph/ModulesGraph+Loading.swift +++ b/Sources/PackageGraph/ModulesGraph+Loading.swift @@ -15,7 +15,9 @@ import OrderedCollections import PackageLoading import PackageModel +import struct TSCBasic.KeyedPair import func TSCBasic.bestMatch +import func TSCBasic.findCycle extension ModulesGraph { /// Load the package graph for the given package path. @@ -44,17 +46,6 @@ extension ModulesGraph { root.manifests.forEach { manifestMap[$0.key] = ($0.value, fileSystem) } - func nodeSuccessorsProvider(node: GraphLoadingNode) -> [GraphLoadingNode] { - node.requiredDependencies.compactMap { dependency in - manifestMap[dependency.identity].map { (manifest, fileSystem) in - GraphLoadingNode( - identity: dependency.identity, - manifest: manifest, - productFilter: dependency.productFilter - ) - } - } - } // Construct the root root dependencies set. let rootDependencies = Set(root.dependencies.compactMap{ @@ -75,33 +66,27 @@ extension ModulesGraph { let inputManifests = rootManifestNodes + rootDependencyNodes // Collect the manifests for which we are going to build packages. - var allNodes: [GraphLoadingNode] + var allNodes = [GraphLoadingNode]() - // Detect cycles in manifest dependencies. - if let cycle = findCycle(inputManifests, successors: nodeSuccessorsProvider) { - observabilityScope.emit(PackageGraphError.cycleDetected(cycle)) - // Break the cycle so we can build a partial package graph. - allNodes = inputManifests.filter({ $0.manifest != cycle.cycle[0] }) - } else { - // Sort all manifests topologically. - allNodes = try topologicalSort(inputManifests, successors: nodeSuccessorsProvider) - } - - var flattenedManifests: [PackageIdentity: GraphLoadingNode] = [:] - for node in allNodes { - if let existing = flattenedManifests[node.identity] { - let merged = GraphLoadingNode( - identity: node.identity, - manifest: node.manifest, - productFilter: existing.productFilter.union(node.productFilter) - ) - flattenedManifests[node.identity] = merged - } else { - flattenedManifests[node.identity] = node + // Cycles in dependencies don't matter as long as there are no target cycles between packages. + depthFirstSearch(inputManifests.map { KeyedPair($0, key: $0.id) }) { + $0.item.requiredDependencies.compactMap { dependency in + manifestMap[dependency.identity].map { (manifest, fileSystem) in + KeyedPair( + GraphLoadingNode( + identity: dependency.identity, + manifest: manifest, + productFilter: dependency.productFilter + ), + key: dependency.identity + ) + } } + } onUnique: { + allNodes.append($0.item) + } onDuplicate: { _,_ in + // no de-duplication is required. } - // sort by identity - allNodes = flattenedManifests.keys.sorted().map { flattenedManifests[$0]! } // force unwrap fine since we are iterating on keys // Create the packages. var manifestToPackage: [Manifest: Package] = [:] @@ -164,18 +149,23 @@ extension ModulesGraph { ) let rootPackages = resolvedPackages.filter { root.manifests.values.contains($0.manifest) } - checkAllDependenciesAreUsed(rootPackages, observabilityScope: observabilityScope) + checkAllDependenciesAreUsed(packages: resolvedPackages, rootPackages, observabilityScope: observabilityScope) return try ModulesGraph( rootPackages: rootPackages, rootDependencies: resolvedPackages.filter { rootDependencies.contains($0.manifest) }, + packages: resolvedPackages, dependencies: requiredDependencies, binaryArtifacts: binaryArtifacts ) } } -private func checkAllDependenciesAreUsed(_ rootPackages: [ResolvedPackage], observabilityScope: ObservabilityScope) { +private func checkAllDependenciesAreUsed( + packages: IdentifiableSet, + _ rootPackages: [ResolvedPackage], + observabilityScope: ObservabilityScope +) { for package in rootPackages { // List all dependency products dependent on by the package targets. let productDependencies = IdentifiableSet(package.targets.flatMap { target in @@ -189,7 +179,12 @@ private func checkAllDependenciesAreUsed(_ rootPackages: [ResolvedPackage], obse } }) - for dependency in package.dependencies { + for dependencyId in package.dependencies { + guard let dependency = packages[dependencyId] else { + observabilityScope.emit(.error("Unknown package: \(dependencyId)")) + return + } + // We continue if the dependency contains executable products to make sure we don't // warn on a valid use-case for a lone dependency: swift run dependency executables. guard !dependency.products.contains(where: { $0.type == .executable }) else { @@ -250,7 +245,7 @@ private func createResolvedPackages( platformVersionProvider: PlatformVersionProvider, fileSystem: FileSystem, observabilityScope: ObservabilityScope -) throws -> [ResolvedPackage] { +) throws -> IdentifiableSet { // Create package builder objects from the input manifests. let packageBuilders: [ResolvedPackageBuilder] = nodes.compactMap{ node in @@ -644,7 +639,32 @@ private func createResolvedPackages( } } - return try packageBuilders.map { try $0.construct() } + do { + let targetBuilders = packageBuilders.flatMap { + $0.targets.map { + KeyedPair($0, key: $0.target) + } + } + if let cycle = findCycle(targetBuilders, successors: { + $0.item.dependencies.flatMap { + switch $0 { + case .product(let productBuilder, conditions: _): + return productBuilder.targets.map { KeyedPair($0, key: $0.target) } + case .target: + return [] // local targets were checked by PackageBuilder. + } + } + }) { + observabilityScope.emit( + ModuleError.cycleDetected( + (cycle.path.map(\.key.name), cycle.cycle.map(\.key.name)) + ) + ) + return IdentifiableSet() + } + } + + return IdentifiableSet(try packageBuilders.map { try $0.construct() }) } private func emitDuplicateProductDiagnostic( @@ -1045,11 +1065,11 @@ private final class ResolvedPackageBuilder: ResolvedBuilder { var targets = products.reduce(into: IdentifiableSet()) { $0.formUnion($1.targets) } try targets.formUnion(self.targets.map { try $0.construct() }) - return try ResolvedPackage( + return ResolvedPackage( underlying: self.package, defaultLocalization: self.defaultLocalization, supportedPlatforms: self.supportedPlatforms, - dependencies: self.dependencies.map{ try $0.construct() }, + dependencies: self.dependencies.map { $0.package.identity }, targets: targets, products: products, registryMetadata: self.registryMetadata, @@ -1057,55 +1077,3 @@ private final class ResolvedPackageBuilder: ResolvedBuilder { ) } } - -/// Finds the first cycle encountered in a graph. -/// -/// This is different from the one in tools support core, in that it handles equality separately from node traversal. Nodes traverse product filters, but only the manifests must be equal for there to be a cycle. -fileprivate func findCycle( - _ nodes: [GraphLoadingNode], - successors: (GraphLoadingNode) throws -> [GraphLoadingNode] -) rethrows -> (path: [Manifest], cycle: [Manifest])? { - // Ordered set to hold the current traversed path. - var path = OrderedCollections.OrderedSet() - - var fullyVisitedManifests = Set() - - // Function to visit nodes recursively. - // FIXME: Convert to stack. - func visit( - _ node: GraphLoadingNode, - _ successors: (GraphLoadingNode) throws -> [GraphLoadingNode] - ) rethrows -> (path: [Manifest], cycle: [Manifest])? { - // Once all successors have been visited, this node cannot participate - // in a cycle. - if fullyVisitedManifests.contains(node.manifest) { - return nil - } - - // If this node is already in the current path then we have found a cycle. - if !path.append(node.manifest).inserted { - let index = path.firstIndex(of: node.manifest)! // forced unwrap safe - return (Array(path[path.startIndex.. - /// The complete list of contained packages, in topological order starting - /// with the root packages. - public let packages: [ResolvedPackage] + /// The complete set of contained packages. + public let packages: IdentifiableSet /// The list of all targets reachable from root targets. public private(set) var reachableTargets: IdentifiableSet @@ -139,17 +138,24 @@ public struct ModulesGraph { return self.rootPackages.contains(id: package.id) } - private var modulesToPackages: [ResolvedModule.ID: ResolvedPackage] + /// Returns the package based on the given identity, or nil if the package isn't in the graph. + public func package(for identity: PackageIdentity) -> ResolvedPackage? { + packages[identity] + } + /// Returns the package that contains the module, or nil if the module isn't in the graph. public func package(for module: ResolvedModule) -> ResolvedPackage? { - return self.modulesToPackages[module.id] + self.package(for: module.packageIdentity) } - - private var productsToPackages: [ResolvedProduct.ID: ResolvedPackage] /// Returns the package that contains the product, or nil if the product isn't in the graph. public func package(for product: ResolvedProduct) -> ResolvedPackage? { - return self.productsToPackages[product.id] + self.package(for: product.packageIdentity) + } + + /// Returns all of the packages that the given package depends on directly. + public func directDependencies(for package: ResolvedPackage) -> [ResolvedPackage] { + package.dependencies.compactMap { self.package(for: $0) } } /// All root and root dependency packages provided as input to the graph. @@ -162,6 +168,7 @@ public struct ModulesGraph { public init( rootPackages: [ResolvedPackage], rootDependencies: [ResolvedPackage] = [], + packages: IdentifiableSet, dependencies requiredDependencies: [PackageReference], binaryArtifacts: [PackageIdentity: [String: BinaryArtifact]] ) throws { @@ -169,22 +176,7 @@ public struct ModulesGraph { self.requiredDependencies = requiredDependencies self.inputPackages = rootPackages + rootDependencies self.binaryArtifacts = binaryArtifacts - self.packages = try topologicalSort(inputPackages, successors: { $0.dependencies }) - let identitiesToPackages = self.packages.spm_createDictionary { ($0.identity, $0) } - - // Create a mapping from targets to the packages that define them. Here - // we include all targets, including tests in non-root packages, since - // this is intended for lookup and not traversal. - var modulesToPackages = self.packages.reduce(into: [:], { partial, package in - package.targets.forEach { partial[$0.id] = package } - }) - - // Create a mapping from products to the packages that define them. Here - // we include all products, including tests in non-root packages, since - // this is intended for lookup and not traversal. - var productsToPackages = packages.reduce(into: [:], { partial, package in - package.products.forEach { partial[$0.id] = package } - }) + self.packages = packages var allTargets = IdentifiableSet() var allProducts = IdentifiableSet() @@ -207,12 +199,8 @@ public struct ModulesGraph { switch dependency { case .target(let targetDependency, _): allTargets.insert(targetDependency) - modulesToPackages[targetDependency.id] = - identitiesToPackages[targetDependency.packageIdentity] case .product(let productDependency, _): allProducts.insert(productDependency) - productsToPackages[productDependency.id] = - identitiesToPackages[productDependency.packageIdentity] } } } @@ -227,9 +215,6 @@ public struct ModulesGraph { } } - self.modulesToPackages = modulesToPackages - self.productsToPackages = productsToPackages - // Compute the reachable targets and products. let inputTargets = self.inputPackages.flatMap { $0.targets } let inputProducts = self.inputPackages.flatMap { $0.products } @@ -264,17 +249,6 @@ public struct ModulesGraph { product.buildTriple = buildTriple return product }) - - self.modulesToPackages = .init(self.modulesToPackages.map { - var target = $0 - target.buildTriple = buildTriple - return (target, $1) - }, uniquingKeysWith: { $1 }) - self.productsToPackages = .init(self.productsToPackages.map { - var product = $0 - product.buildTriple = buildTriple - return (product, $1) - }, uniquingKeysWith: { $1 }) } /// Computes a map from each executable target in any of the root packages to the corresponding test targets. diff --git a/Sources/PackageGraph/Resolution/ResolvedPackage.swift b/Sources/PackageGraph/Resolution/ResolvedPackage.swift index 78cd87c2345..32536f0dbb1 100644 --- a/Sources/PackageGraph/Resolution/ResolvedPackage.swift +++ b/Sources/PackageGraph/Resolution/ResolvedPackage.swift @@ -40,7 +40,7 @@ public struct ResolvedPackage { public let products: [ResolvedProduct] /// The dependencies of the package. - public let dependencies: [ResolvedPackage] + public let dependencies: [PackageIdentity] /// The default localization for resources. public let defaultLocalization: String? @@ -57,7 +57,7 @@ public struct ResolvedPackage { underlying: Package, defaultLocalization: String?, supportedPlatforms: [SupportedPlatform], - dependencies: [ResolvedPackage], + dependencies: [PackageIdentity], targets: IdentifiableSet, products: [ResolvedProduct], registryMetadata: RegistryReleaseMetadata?, diff --git a/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift b/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift index 0ea946cb38e..340f02d1cf8 100644 --- a/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift +++ b/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift @@ -22,6 +22,7 @@ typealias WireInput = HostToPluginMessage.InputContext /// the input information to a plugin. internal struct PluginContextSerializer { let fileSystem: FileSystem + let modulesGraph: ModulesGraph let buildEnvironment: BuildEnvironment let pkgConfigDirectories: [AbsolutePath] let sdkRootPath: AbsolutePath? @@ -244,7 +245,7 @@ internal struct PluginContextSerializer { } // Serialize the dependencies. It is important to do this before the `let id = package.count` below so the correct wire ID gets assigned. - let dependencies = try package.dependencies.map { + let dependencies = try modulesGraph.directDependencies(for: package).map { WireInput.Package.Dependency(packageId: try serialize(package: $0)) } diff --git a/Sources/SPMBuildCore/Plugins/PluginInvocation.swift b/Sources/SPMBuildCore/Plugins/PluginInvocation.swift index fc832b07dc4..3ab08cd47d5 100644 --- a/Sources/SPMBuildCore/Plugins/PluginInvocation.swift +++ b/Sources/SPMBuildCore/Plugins/PluginInvocation.swift @@ -43,6 +43,7 @@ extension PluginTarget { pkgConfigDirectories: [AbsolutePath], sdkRootPath: AbsolutePath?, fileSystem: FileSystem, + modulesGraph: ModulesGraph, observabilityScope: ObservabilityScope, callbackQueue: DispatchQueue, delegate: PluginInvocationDelegate @@ -62,6 +63,7 @@ extension PluginTarget { pkgConfigDirectories: pkgConfigDirectories, sdkRootPath: sdkRootPath, fileSystem: fileSystem, + modulesGraph: modulesGraph, observabilityScope: observabilityScope, callbackQueue: callbackQueue, delegate: delegate, @@ -107,6 +109,7 @@ extension PluginTarget { pkgConfigDirectories: [AbsolutePath], sdkRootPath: AbsolutePath?, fileSystem: FileSystem, + modulesGraph: ModulesGraph, observabilityScope: ObservabilityScope, callbackQueue: DispatchQueue, delegate: PluginInvocationDelegate, @@ -125,6 +128,7 @@ extension PluginTarget { do { var serializer = PluginContextSerializer( fileSystem: fileSystem, + modulesGraph: modulesGraph, buildEnvironment: buildEnvironment, pkgConfigDirectories: pkgConfigDirectories, sdkRootPath: sdkRootPath @@ -571,6 +575,7 @@ extension ModulesGraph { pkgConfigDirectories: pkgConfigDirectories, sdkRootPath: buildParameters.toolchain.sdkRootPath, fileSystem: fileSystem, + modulesGraph: self, observabilityScope: observabilityScope, callbackQueue: delegateQueue, delegate: delegate, diff --git a/Sources/SPMTestSupport/PackageGraphTester.swift b/Sources/SPMTestSupport/PackageGraphTester.swift index a45ee9d57ad..1ebeda753fe 100644 --- a/Sources/SPMTestSupport/PackageGraphTester.swift +++ b/Sources/SPMTestSupport/PackageGraphTester.swift @@ -147,7 +147,7 @@ package final class PackageGraphResult { } package func find(package: PackageIdentity) -> ResolvedPackage? { - return graph.packages.first(where: { $0.identity == package }) + return graph.package(for: package) } private func reachableBuildTargets(in environment: BuildEnvironment) throws -> IdentifiableSet { diff --git a/Sources/Workspace/Workspace+Manifests.swift b/Sources/Workspace/Workspace+Manifests.swift index 511dd191d50..8b48dea8df2 100644 --- a/Sources/Workspace/Workspace+Manifests.swift +++ b/Sources/Workspace/Workspace+Manifests.swift @@ -16,6 +16,7 @@ import struct Basics.InternalError import class Basics.ObservabilityScope import struct Basics.SwiftVersion import func Basics.temp_await +import func Basics.depthFirstSearch import class Basics.ThreadSafeKeyValueStore import class Dispatch.DispatchGroup import struct Dispatch.DispatchTime @@ -494,12 +495,7 @@ extension Workspace { // Continue to load the rest of the manifest for this graph // Creates a map of loaded manifests. We do this to avoid reloading the shared nodes. var loadedManifests = firstLevelManifests - // Compute the transitive closure of available dependencies. - let topologicalSortInput = topLevelManifests.map { identity, manifest in KeyedPair( - manifest, - key: Key(identity: identity, productFilter: .everything) - ) } - let topologicalSortSuccessors: (KeyedPair) throws -> [KeyedPair] = { pair in + let successorManifests: (KeyedPair) throws -> [KeyedPair] = { pair in // optimization: preload manifest we know about in parallel let dependenciesRequired = pair.item.dependenciesRequired(for: pair.key.productFilter) let dependenciesToLoad = dependenciesRequired.map(\.packageRef) @@ -527,36 +523,26 @@ extension Workspace { } } - // Look for any cycle in the dependencies. - if let cycle = try findCycle(topologicalSortInput, successors: topologicalSortSuccessors) { - observabilityScope.emit( - error: "cyclic dependency declaration found: " + - (cycle.path + cycle.cycle).map(\.key.identity.description).joined(separator: " -> ") + - " -> " + cycle.cycle[0].key.identity.description - ) - // return partial results - return DependencyManifests( - root: root, - dependencies: [], - workspace: self, - observabilityScope: observabilityScope - ) - } - let allManifestsWithPossibleDuplicates = try topologicalSort( - topologicalSortInput, - successors: topologicalSortSuccessors - ) - - // merge the productFilter of the same package (by identity) - var deduplication = [PackageIdentity: Int]() var allManifests = [(identity: PackageIdentity, manifest: Manifest, productFilter: ProductFilter)]() - for pair in allManifestsWithPossibleDuplicates { - if let index = deduplication[pair.key.identity] { - let productFilter = allManifests[index].productFilter.merge(pair.key.productFilter) - allManifests[index] = (pair.key.identity, pair.item, productFilter) - } else { + do { + let manifestGraphRoots = topLevelManifests.map { identity, manifest in + KeyedPair( + manifest, + key: Key(identity: identity, productFilter: .everything) + ) + } + + var deduplication = [PackageIdentity: Int]() + try depthFirstSearch( + manifestGraphRoots, + successors: successorManifests + ) { pair in deduplication[pair.key.identity] = allManifests.count allManifests.append((pair.key.identity, pair.item, pair.key.productFilter)) + } onDuplicate: { old, new in + let index = deduplication[old.key.identity]! + let productFilter = allManifests[index].productFilter.merge(new.key.productFilter) + allManifests[index] = (new.key.identity, new.item, productFilter) } } diff --git a/Sources/Workspace/Workspace+Signing.swift b/Sources/Workspace/Workspace+Signing.swift index dd13d0606b4..1db01de2dd5 100644 --- a/Sources/Workspace/Workspace+Signing.swift +++ b/Sources/Workspace/Workspace+Signing.swift @@ -46,7 +46,7 @@ extension Workspace { expectedSigningEntities: [PackageIdentity: RegistryReleaseMetadata.SigningEntity] ) throws { try expectedSigningEntities.forEach { identity, expectedSigningEntity in - if let package = packageGraph.packages.first(where: { $0.identity == identity }) { + if let package = packageGraph.package(for: identity) { guard let actualSigningEntity = package.registryMetadata?.signature?.signedBy else { throw SigningError.unsigned(package: identity, expected: expectedSigningEntity) } @@ -68,7 +68,7 @@ extension Workspace { expected: expectedSigningEntity ) } - guard let package = packageGraph.packages.first(where: { $0.identity == mirroredIdentity }) else { + guard let package = packageGraph.package(for: mirroredIdentity) else { // Unsure if this case is reachable in practice. throw SigningError.expectedIdentityNotFound(package: identity) } diff --git a/Sources/Workspace/Workspace.swift b/Sources/Workspace/Workspace.swift index 26bcd60543c..816fb2dcbe7 100644 --- a/Sources/Workspace/Workspace.swift +++ b/Sources/Workspace/Workspace.swift @@ -1150,7 +1150,7 @@ extension Workspace { observabilityScope: ObservabilityScope, completion: @escaping (Result) -> Void ) { - guard let previousPackage = packageGraph.packages.first(where: { $0.identity == identity }) else { + guard let previousPackage = packageGraph.package(for: identity) else { return completion(.failure(StringError("could not find package with identity \(identity)"))) } diff --git a/Tests/CommandsTests/MermaidPackageSerializerTests.swift b/Tests/CommandsTests/MermaidPackageSerializerTests.swift index 2c5f88cd415..d14e7f30d9e 100644 --- a/Tests/CommandsTests/MermaidPackageSerializerTests.swift +++ b/Tests/CommandsTests/MermaidPackageSerializerTests.swift @@ -110,7 +110,7 @@ final class MermaidPackageSerializerTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertEqual(graph.packages.count, 2) - let package = try XCTUnwrap(graph.packages.first) + let package = try XCTUnwrap(graph.package(for: .plain("A"))) let serializer = MermaidPackageSerializer(package: package.underlying) XCTAssertEqual( serializer.renderedMarkdown, @@ -169,7 +169,7 @@ final class MermaidPackageSerializerTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) XCTAssertEqual(graph.packages.count, 2) - let package = try XCTUnwrap(graph.packages.first) + let package = try XCTUnwrap(graph.package(for: .plain("A"))) let serializer = MermaidPackageSerializer(package: package.underlying) XCTAssertEqual( serializer.renderedMarkdown, diff --git a/Tests/CommandsTests/PackageCommandTests.swift b/Tests/CommandsTests/PackageCommandTests.swift index f67e148dac1..92b47f8695d 100644 --- a/Tests/CommandsTests/PackageCommandTests.swift +++ b/Tests/CommandsTests/PackageCommandTests.swift @@ -647,6 +647,7 @@ final class PackageCommandTests: CommandsTestCase { let output = BufferedOutputByteStream() SwiftPackageCommand.ShowDependencies.dumpDependenciesOf( + graph: graph, rootPackage: graph.rootPackages[graph.rootPackages.startIndex], mode: .dot, on: output diff --git a/Tests/FunctionalTests/PluginTests.swift b/Tests/FunctionalTests/PluginTests.swift index c81842f822a..d96ff4ca8a7 100644 --- a/Tests/FunctionalTests/PluginTests.swift +++ b/Tests/FunctionalTests/PluginTests.swift @@ -548,6 +548,7 @@ final class PluginTests: XCTestCase { pkgConfigDirectories: [], sdkRootPath: nil, fileSystem: localFileSystem, + modulesGraph: packageGraph, observabilityScope: observability.topScope, callbackQueue: delegateQueue, delegate: delegate, @@ -825,6 +826,7 @@ final class PluginTests: XCTestCase { pkgConfigDirectories: [], sdkRootPath: try UserToolchain.default.sdkRootPath, fileSystem: localFileSystem, + modulesGraph: packageGraph, observabilityScope: observability.topScope, callbackQueue: delegateQueue, delegate: delegate diff --git a/Tests/PackageGraphTests/ModulesGraphTests.swift b/Tests/PackageGraphTests/ModulesGraphTests.swift index 16c86130bbc..946fd37d9b7 100644 --- a/Tests/PackageGraphTests/ModulesGraphTests.swift +++ b/Tests/PackageGraphTests/ModulesGraphTests.swift @@ -83,12 +83,12 @@ final class ModulesGraphTests: XCTestCase { result.checkTarget("Baz") { result in result.check(dependencies: "Bar") } } - let fooPackage = try XCTUnwrap(g.packages.first{ $0.identity == .plain("Foo") }) + let fooPackage = try XCTUnwrap(g.package(for: .plain("Foo"))) let fooTarget = try XCTUnwrap(g.allTargets.first{ $0.name == "Foo" }) let fooDepTarget = try XCTUnwrap(g.allTargets.first{ $0.name == "FooDep" }) XCTAssertEqual(g.package(for: fooTarget)?.id, fooPackage.id) XCTAssertEqual(g.package(for: fooDepTarget)?.id, fooPackage.id) - let barPackage = try XCTUnwrap(g.packages.first{ $0.identity == .plain("Bar") }) + let barPackage = try XCTUnwrap(g.package(for: .plain("Bar"))) let barTarget = try XCTUnwrap(g.allTargets.first{ $0.name == "Bar" }) XCTAssertEqual(g.package(for: barTarget)?.id, barPackage.id) } @@ -190,32 +190,77 @@ final class ModulesGraphTests: XCTestCase { } } - func testCycle2() throws { + func testLocalTargetCycle() throws { let fs = InMemoryFileSystem(emptyFiles: "/Foo/Sources/Foo/source.swift", - "/Bar/Sources/Bar/source.swift", - "/Baz/Sources/Baz/source.swift" + "/Foo/Sources/Bar/source.swift" ) let observability = ObservabilitySystem.makeForTesting() _ = try loadModulesGraph( + fileSystem: fs, + manifests: [ + Manifest.createRootManifest( + displayName: "Foo", + path: "/Foo", + targets: [ + TargetDescription(name: "Foo", dependencies: ["Bar"]), + TargetDescription(name: "Bar", dependencies: ["Foo"]) + ]), + ], + observabilityScope: observability.topScope + ) + + testDiagnostics(observability.diagnostics) { result in + result.check(diagnostic: "cyclic dependency declaration found: Bar -> Foo -> Bar", severity: .error) + } + } + + func testDependencyCycleWithoutTargetCycle() throws { + let fs = InMemoryFileSystem(emptyFiles: + "/Foo/Sources/Foo/source.swift", + "/Bar/Sources/Bar/source.swift", + "/Bar/Sources/Baz/source.swift" + ) + + let observability = ObservabilitySystem.makeForTesting() + let graph = try loadModulesGraph( fileSystem: fs, manifests: [ Manifest.createRootManifest( displayName: "Foo", path: "/Foo", dependencies: [ - .localSourceControl(path: "/Foo", requirement: .upToNextMajor(from: "1.0.0")) + .localSourceControl(path: "/Bar", requirement: .upToNextMajor(from: "1.0.0")) + ], + products: [ + ProductDescription(name: "Foo", type: .library(.automatic), targets: ["Foo"]) ], targets: [ - TargetDescription(name: "Foo"), + TargetDescription(name: "Foo", dependencies: ["Bar"]), ]), + Manifest.createFileSystemManifest( + displayName: "Bar", + path: "/Bar", + dependencies: [ + .localSourceControl(path: "/Foo", requirement: .upToNextMajor(from: "1.0.0")) + ], + products: [ + ProductDescription(name: "Bar", type: .library(.automatic), targets: ["Bar"]), + ProductDescription(name: "Baz", type: .library(.automatic), targets: ["Baz"]) + ], + targets: [ + TargetDescription(name: "Bar"), + TargetDescription(name: "Baz", dependencies: ["Foo"]), + ]) ], observabilityScope: observability.topScope ) - testDiagnostics(observability.diagnostics) { result in - result.check(diagnostic: "cyclic dependency declaration found: Foo -> Foo", severity: .error) + XCTAssertNoDiagnostics(observability.diagnostics) + PackageGraphTester(graph) { result in + result.check(packages: "Foo", "Bar") + result.check(targets: "Bar", "Baz", "Foo") } } diff --git a/Tests/WorkspaceTests/WorkspaceTests.swift b/Tests/WorkspaceTests/WorkspaceTests.swift index 783329c01f1..584ff82d7bd 100644 --- a/Tests/WorkspaceTests/WorkspaceTests.swift +++ b/Tests/WorkspaceTests/WorkspaceTests.swift @@ -1971,7 +1971,7 @@ final class WorkspaceTests: XCTestCase { // Ensure that the order of the manifests is stable. XCTAssertEqual( manifests.allDependencyManifests.map(\.value.manifest.displayName), - ["Foo", "Baz", "Bam", "Bar"] + ["Bam", "Baz", "Bar", "Foo"] ) XCTAssertNoDiagnostics(diagnostics) } @@ -11123,7 +11123,7 @@ final class WorkspaceTests: XCTestCase { // FIXME: rdar://72940946 // we need to improve this situation or diagnostics when working on identity result.check( - diagnostic: "cyclic dependency declaration found: Root -> FooUtilityPackage -> BarPackage -> FooUtilityPackage", + diagnostic: "'bar' dependency on '/tmp/ws/pkgs/other/utility' conflicts with dependency on '/tmp/ws/pkgs/foo/utility' which has the same identity 'utility'", severity: .error ) } @@ -11207,7 +11207,11 @@ final class WorkspaceTests: XCTestCase { // FIXME: rdar://72940946 // we need to improve this situation or diagnostics when working on identity result.check( - diagnostic: "cyclic dependency declaration found: Root -> FooUtilityPackage -> BarPackage -> FooUtilityPackage", + diagnostic: "'bar' dependency on '/tmp/ws/pkgs/other/utility' conflicts with dependency on '/tmp/ws/pkgs/foo/utility' which has the same identity 'utility'. this will be escalated to an error in future versions of SwiftPM.", + severity: .warning + ) + result.check( + diagnostic: "product 'OtherUtilityProduct' required by package 'bar' target 'BarTarget' not found in package 'OtherUtilityPackage'.", severity: .error ) } @@ -11282,7 +11286,7 @@ final class WorkspaceTests: XCTestCase { // FIXME: rdar://72940946 // we need to improve this situation or diagnostics when working on identity result.check( - diagnostic: "cyclic dependency declaration found: Root -> BarPackage -> Root", + diagnostic: "product 'FooProduct' required by package 'bar' target 'BarTarget' not found in package 'FooPackage'.", severity: .error ) } @@ -12015,7 +12019,7 @@ final class WorkspaceTests: XCTestCase { targets: [ .init(name: "Root1Target", dependencies: [ .product(name: "FooProduct", package: "foo"), - .product(name: "Root2Target", package: "Root2") + .product(name: "Root2Product", package: "Root2") ]), ], products: [ @@ -12111,15 +12115,7 @@ final class WorkspaceTests: XCTestCase { try workspace.checkPackageGraph(roots: ["Root1", "Root2"]) { _, diagnostics in testDiagnostics(diagnostics) { result in result.check( - diagnostic: .regex("cyclic dependency declaration found: root[1|2] -> *"), - severity: .error - ) - result.check( - diagnostic: """ - exhausted attempts to resolve the dependencies graph, with the following dependencies unresolved: - * 'bar' from http://scm.com/org/bar - * 'foo' from http://scm.com/org/foo - """, + diagnostic: .regex("cyclic dependency declaration found: Root[1|2]Target -> *"), severity: .error ) } From 4a6660ac95f4b2f88e1006f84576b0ca16343eb9 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 9 May 2024 00:37:37 +0100 Subject: [PATCH 139/159] Align `swift sdk` configuration subcommand with proposal text (#7535) Per [the proposal text](https://github.com/apple/swift-evolution/blob/main/proposals/0387-cross-compilation-destinations.md#swift-sdk-installation-and-configuration): > `swift sdk configure `, which allows users to provide additional search paths and toolsets to be used subsequently when building with a given Swift SDK. Specifically, multiple `--swift-resources-path`, `--include-search-path`, `--library-search-path`, and `--toolset` options with corresponding paths can be provided, which then will be stored as configuration for this Swift SDK. `swift sdk configure --show-configuration` will print currently set paths, while `swift sdk configure --reset` will reset all of those at once. --- CHANGELOG.md | 7 +- .../SwiftSDKs/test-sdk.artifactbundle.tar.gz | Bin 412 -> 788 bytes .../SwiftSDKs/test-sdk.artifactbundle.zip | Bin 1056 -> 1014 bytes .../SwiftSDKs/SwiftSDKBundle.swift | 8 +- .../SwiftSDKs/SwiftSDKBundleStore.swift | 7 +- .../SwiftSDKConfigurationStore.swift | 2 +- Sources/SwiftSDKCommand/CMakeLists.txt | 3 +- .../ConfigurationSubcommand.swift | 3 + ...recatedSwiftSDKConfigurationCommand.swift} | 10 +- .../SwiftSDKCommand/ConfigureSwiftSDK.swift | 252 +++++++++++++++++ Sources/SwiftSDKCommand/InstallSwiftSDK.swift | 6 +- Sources/SwiftSDKCommand/ListSwiftSDKs.swift | 6 +- Sources/SwiftSDKCommand/RemoveSwiftSDK.swift | 6 +- Sources/SwiftSDKCommand/SwiftSDKCommand.swift | 1 + .../SwiftSDKCommand/SwiftSDKSubcommand.swift | 2 +- .../swift-experimental-sdk/Entrypoint.swift | 2 +- Sources/swift-package-manager/SwiftPM.swift | 2 +- Tests/CommandsTests/SDKCommandTests.swift | 121 -------- .../CommandsTests/SwiftSDKCommandTests.swift | 265 ++++++++++++++++++ 19 files changed, 554 insertions(+), 149 deletions(-) rename Sources/SwiftSDKCommand/Configuration/{ConfigureSwiftSDK.swift => DeprecatedSwiftSDKConfigurationCommand.swift} (79%) create mode 100644 Sources/SwiftSDKCommand/ConfigureSwiftSDK.swift delete mode 100644 Tests/CommandsTests/SDKCommandTests.swift create mode 100644 Tests/CommandsTests/SwiftSDKCommandTests.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 26faec224b2..460f42544e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ Swift 6.0 `--experimental-swift-sdks-path` options on `swift build` are deprecated with replacements that don't have the `experimental` prefix. +* [#7535] The `swift sdk configuration` subcommand is deprecated with a replacement named `configure` that has options that exactly match + [SE-0387 proposal text]. + * [#7202] Package manifests can now access information about the Git repository the given package is in via the context object's @@ -366,6 +369,7 @@ Swift 3.0 [SE-0386]: https://github.com/apple/swift-evolution/blob/main/proposals/0386-package-access-modifier.md [SE-0387]: https://github.com/apple/swift-evolution/blob/main/proposals/0387-cross-compilation-destinations.md [SE-0391]: https://github.com/apple/swift-evolution/blob/main/proposals/0391-package-registry-publish.md +[SE-0387 proposal text]: https://github.com/apple/swift-evolution/blob/main/proposals/0387-cross-compilation-destinations.md#swift-sdk-installation-and-configuration [SR-5918]: https://bugs.swift.org/browse/SR-5918 [SR-6978]: https://bugs.swift.org/browse/SR-6978 @@ -415,4 +419,5 @@ Swift 3.0 [#7118]: https://github.com/apple/swift-package-manager/pull/7118 [#7201]: https://github.com/apple/swift-package-manager/pull/7201 [#7202]: https://github.com/apple/swift-package-manager/pull/7202 -[#7505]: https://github.com/apple/swift-package-manager/pull/7507 +[#7507]: https://github.com/apple/swift-package-manager/pull/7507 +[#7535]: https://github.com/apple/swift-package-manager/pull/7535 diff --git a/Fixtures/SwiftSDKs/test-sdk.artifactbundle.tar.gz b/Fixtures/SwiftSDKs/test-sdk.artifactbundle.tar.gz index 04f705d0b65ecc94c5becc71e923cf62d8a1c2c3..d02b411724d76eca42c25f1c7d0c304435f2da12 100644 GIT binary patch literal 788 zcmV+v1MB=BiwFR5YCC2C1MQhlZ__Xs#LS*?QOZ5HgY(H7w z-`^d6jCy>L<^EWcg{%03hS!0j+dZ*i*Y{3sSGWyZTUgW{{rvUg+vo4U3phzd7CWN9 zZi}YvFWuZakf(pl$e8{uj*tfW1&p0b#r4lDu(|QHzPq-*aqt}WMHu?D@9XY_xg`JZyG{9hSl`#;BRdGKT4xpR*puLq|z&;N1h`Z&WTjrRW*qssr4 zg7SZrCg1B=4c?_}w} zxa^;=e@Z6uKQk?r|I6S)kFvdSdG`YYJON28j>nJ3Wo5c9sXy=9QP>F1oL0E^?4|KI z-Qd*r1IKgYHV7h!CmIOf5pFW@yHr)g#Y80T)D_`zGHkPLOf(s!BJcHde2`4Q2pY$B zM;wgI4?|?=gK8~B)AoPxFutpC^*l?#Z2mua|1Wb;lK)Mm|58x?ztT+8|HwPwxz>R^ z{g3|tpXlEtCRhHy43xvKHq-Q9O!l8;|If$sKZ)M|s{DWRTwG^{r5C2s3lEL5ygo;E zxbHhs+<}qW9thz#jybLM+_vkTxwR9=?e%L%ZqJz5DaglY*Ym=i$hnUj_n|S@2e*AS S6bgl+I{X2byh|ehFaQ98%Bn#C literal 412 zcmV;N0b~9jiwFQ|!X{+^1MO5jPs1<}?VMj>c_zV5ZAW5eV?lse5QAx)Hner3*r7#L z{yT1*eulIlqE@JR%USQvKHKl^v{2er;n?Gujw3$MZ@CO7;=0!&5JCZ^ptQ_du=uWu zO%W4Q#8rix{}~Fs>7uRi(A-Y7Nny(9AxZT8vU97(-R~%;IQMUK=+L3V<=_j~IzBo8 G5C8xL`q1S7 diff --git a/Fixtures/SwiftSDKs/test-sdk.artifactbundle.zip b/Fixtures/SwiftSDKs/test-sdk.artifactbundle.zip index e6c3a41a9de7d2625e0396b107b7b18d78bc94b7..6d38f278a20c9ef3248587952ae7f1310e7dbad2 100644 GIT binary patch delta 459 zcmZ3$@r|7)z?+$civa|>TPE_@@_uDxU|CBcRPbMX)vTC5YIv%W?+M?6bmrZ5aU7`JQ zftH51uGYCT=b2Xh9!-Kj3E0Y cCyO)lX<`Yh0B=?{kYW}foDS6C$pqp704Wk|$N&HU delta 520 zcmeyyzJP-#z?+$civa|>YbNs8s=Q!kU|<0eAPEtmFb9Krt#^1pf_B_;ATNrMfkAO% zuJq*n%-r>y49rr8)KWi*>`g7L;AUV1>jM+1{kHr~1|qKIb*z<}RVPTWr<=R?b{AGO zD)C0l`W9-!`#(10vHR2B=XdVS-lZ~^FJSVDjVjD9%}VE0`rQ$zK7E7re@M--1h&A- ziQC&lWp)(jy6D%co&U5Zf+c`^O5zWhl|Sz^x&3hGTFJRN^mq7=lM<^#)^~=x>xLZX z_4(Vd;&j^Pw6JZPU4rZ2>M{*-88&Dww3lL5RI;NNj!~+2G7L!f@ diff --git a/Sources/PackageModel/SwiftSDKs/SwiftSDKBundle.swift b/Sources/PackageModel/SwiftSDKs/SwiftSDKBundle.swift index 755c4153845..a353c08ba53 100644 --- a/Sources/PackageModel/SwiftSDKs/SwiftSDKBundle.swift +++ b/Sources/PackageModel/SwiftSDKs/SwiftSDKBundle.swift @@ -53,7 +53,7 @@ extension [SwiftSDKBundle] { /// - hostTriple: triple of the machine on which the Swift SDK is building. /// - targetTriple: triple of the machine for which the Swift SDK is building. /// - Returns: ``SwiftSDK`` value with a given artifact ID, `nil` if none found. - public func selectSwiftSDK(id: String, hostTriple: Triple, targetTriple: Triple) -> SwiftSDK? { + public func selectSwiftSDK(id: String, hostTriple: Triple?, targetTriple: Triple) -> SwiftSDK? { for bundle in self { for (artifactID, variants) in bundle.artifacts { guard artifactID == id else { @@ -61,8 +61,10 @@ extension [SwiftSDKBundle] { } for variant in variants { - guard variant.isSupporting(hostTriple: hostTriple) else { - continue + if let hostTriple { + guard variant.isSupporting(hostTriple: hostTriple) else { + continue + } } return variant.swiftSDKs.first { $0.targetTriple == targetTriple } diff --git a/Sources/PackageModel/SwiftSDKs/SwiftSDKBundleStore.swift b/Sources/PackageModel/SwiftSDKs/SwiftSDKBundleStore.swift index 13d3b55b966..c1fcb320104 100644 --- a/Sources/PackageModel/SwiftSDKs/SwiftSDKBundleStore.swift +++ b/Sources/PackageModel/SwiftSDKs/SwiftSDKBundleStore.swift @@ -239,9 +239,10 @@ public final class SwiftSDKBundleStore { try await archiver.extract(from: bundlePath, to: extractionResultsDirectory) - guard let bundleName = try fileSystem.getDirectoryContents(extractionResultsDirectory).first, - bundleName.hasSuffix(".\(artifactBundleExtension)") - else { + guard let bundleName = try fileSystem.getDirectoryContents(extractionResultsDirectory).first(where: { + $0.hasSuffix(".\(artifactBundleExtension)") && + fileSystem.isDirectory(extractionResultsDirectory.appending($0)) + }) else { throw SwiftSDKError.invalidBundleArchive(bundlePath) } diff --git a/Sources/PackageModel/SwiftSDKs/SwiftSDKConfigurationStore.swift b/Sources/PackageModel/SwiftSDKs/SwiftSDKConfigurationStore.swift index 5002ce264dd..105fae09b6c 100644 --- a/Sources/PackageModel/SwiftSDKs/SwiftSDKConfigurationStore.swift +++ b/Sources/PackageModel/SwiftSDKs/SwiftSDKConfigurationStore.swift @@ -97,7 +97,7 @@ public final class SwiftSDKConfigurationStore { guard var swiftSDK = swiftSDKs.selectSwiftSDK( id: sdkID, - hostTriple: hostTriple, + hostTriple: nil, targetTriple: targetTriple ) else { return nil diff --git a/Sources/SwiftSDKCommand/CMakeLists.txt b/Sources/SwiftSDKCommand/CMakeLists.txt index eca68245472..4dcfa36a2af 100644 --- a/Sources/SwiftSDKCommand/CMakeLists.txt +++ b/Sources/SwiftSDKCommand/CMakeLists.txt @@ -8,10 +8,11 @@ add_library(SwiftSDKCommand Configuration/ConfigurationSubcommand.swift - Configuration/ConfigureSwiftSDK.swift + Configuration/DeprecatedSwiftSDKConfigurationCommand.swift Configuration/ResetConfiguration.swift Configuration/SetConfiguration.swift Configuration/ShowConfiguration.swift + ConfigureSwiftSDK.swift SwiftSDKSubcommand.swift InstallSwiftSDK.swift ListSwiftSDKs.swift diff --git a/Sources/SwiftSDKCommand/Configuration/ConfigurationSubcommand.swift b/Sources/SwiftSDKCommand/Configuration/ConfigurationSubcommand.swift index 3c8e6b4a71e..2c7098a02f8 100644 --- a/Sources/SwiftSDKCommand/Configuration/ConfigurationSubcommand.swift +++ b/Sources/SwiftSDKCommand/Configuration/ConfigurationSubcommand.swift @@ -12,6 +12,7 @@ import ArgumentParser import Basics +import Foundation import PackageModel protocol ConfigurationSubcommand: SwiftSDKSubcommand { @@ -47,6 +48,8 @@ extension ConfigurationSubcommand { _ swiftSDKsDirectory: AbsolutePath, _ observabilityScope: ObservabilityScope ) throws { + fputs("warning: `swift sdk configuration` command is deprecated and will be removed in a future version of SwiftPM. Use `swift sdk configure` instead.\n", stderr) + let bundleStore = SwiftSDKBundleStore( swiftSDKsDirectory: swiftSDKsDirectory, fileSystem: self.fileSystem, diff --git a/Sources/SwiftSDKCommand/Configuration/ConfigureSwiftSDK.swift b/Sources/SwiftSDKCommand/Configuration/DeprecatedSwiftSDKConfigurationCommand.swift similarity index 79% rename from Sources/SwiftSDKCommand/Configuration/ConfigureSwiftSDK.swift rename to Sources/SwiftSDKCommand/Configuration/DeprecatedSwiftSDKConfigurationCommand.swift index d471356220a..86cd4332b57 100644 --- a/Sources/SwiftSDKCommand/Configuration/ConfigureSwiftSDK.swift +++ b/Sources/SwiftSDKCommand/Configuration/DeprecatedSwiftSDKConfigurationCommand.swift @@ -11,11 +11,15 @@ //===----------------------------------------------------------------------===// import ArgumentParser +import Basics +import PackageModel -package struct ConfigureSwiftSDK: ParsableCommand { - package static let configuration = CommandConfiguration( +struct DeprecatedSwiftSDKConfigurationCommand: ParsableCommand { + static let configuration = CommandConfiguration( commandName: "configuration", abstract: """ + Deprecated: use `swift sdk configure` instead. + Manages configuration options for installed Swift SDKs. """, subcommands: [ @@ -24,6 +28,4 @@ package struct ConfigureSwiftSDK: ParsableCommand { ShowConfiguration.self, ] ) - - package init() {} } diff --git a/Sources/SwiftSDKCommand/ConfigureSwiftSDK.swift b/Sources/SwiftSDKCommand/ConfigureSwiftSDK.swift new file mode 100644 index 00000000000..3708bf7eba9 --- /dev/null +++ b/Sources/SwiftSDKCommand/ConfigureSwiftSDK.swift @@ -0,0 +1,252 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import ArgumentParser +import Basics +import CoreCommands +import Dispatch +import PackageModel + +import var TSCBasic.stdoutStream + +struct ConfigureSwiftSDK: AsyncParsableCommand { + static let configuration = CommandConfiguration( + commandName: "configure", + abstract: """ + Manages configuration options for installed Swift SDKs. + """ + ) + + @OptionGroup(visibility: .hidden) + var locations: LocationOptions + + @Option(help: "A path to a directory containing the SDK root.") + var sdkRootPath: String? = nil + + @Option(help: "A path to a directory containing Swift resources for dynamic linking.") + var swiftResourcesPath: String? = nil + + @Option(help: "A path to a directory containing Swift resources for static linking.") + var swiftStaticResourcesPath: String? = nil + + @Option( + parsing: .singleValue, + help: """ + A path to a directory containing headers. Multiple paths can be specified by providing this option multiple \ + times to the command. + """ + ) + var includeSearchPath: [String] = [] + + @Option( + parsing: .singleValue, + help: """ + "A path to a directory containing libraries. Multiple paths can be specified by providing this option multiple \ + times to the command. + """ + ) + var librarySearchPath: [String] = [] + + @Option( + parsing: .singleValue, + help: """ + "A path to a toolset file. Multiple paths can be specified by providing this option multiple times to the command. + """ + ) + var toolsetPath: [String] = [] + + @Flag( + name: .customLong("reset"), + help: """ + Resets configuration properties currently applied to a given Swift SDK and target triple. If no specific \ + property is specified, all of them are reset for the Swift SDK. + """ + ) + var shouldReset: Bool = false + + @Flag( + name: .customLong("show-configuration"), + help: """ + Prints all configuration properties currently applied to a given Swift SDK and target triple. + """ + ) + var shouldShowConfiguration: Bool = false + + @Argument( + help: """ + An identifier of an already installed Swift SDK. Use the `list` subcommand to see all available \ + identifiers. + """ + ) + var sdkID: String + + @Argument(help: "The target triple of the Swift SDK to configure.") + var targetTriple: String + + /// The file system used by default by this command. + private var fileSystem: FileSystem { localFileSystem } + + /// Parses Swift SDKs directory option if provided or uses the default path for Swift SDKs + /// on the file system. A new directory at this path is created if one doesn't exist already. + /// - Returns: existing or a newly created directory at the computed location. + private func getOrCreateSwiftSDKsDirectory() throws -> AbsolutePath { + var swiftSDKsDirectory = try fileSystem.getSharedSwiftSDKsDirectory( + explicitDirectory: locations.swiftSDKsDirectory + ) + + if !self.fileSystem.exists(swiftSDKsDirectory) { + swiftSDKsDirectory = try self.fileSystem.getOrCreateSwiftPMSwiftSDKsDirectory() + } + + return swiftSDKsDirectory + } + + func run() async throws { + let observabilityHandler = SwiftCommandObservabilityHandler(outputStream: stdoutStream, logLevel: .info) + let observabilitySystem = ObservabilitySystem(observabilityHandler) + let observabilityScope = observabilitySystem.topScope + let swiftSDKsDirectory = try self.getOrCreateSwiftSDKsDirectory() + + let hostToolchain = try UserToolchain(swiftSDK: SwiftSDK.hostSwiftSDK()) + let triple = try Triple.getHostTriple(usingSwiftCompiler: hostToolchain.swiftCompilerPath) + + var commandError: Error? = nil + do { + let bundleStore = SwiftSDKBundleStore( + swiftSDKsDirectory: swiftSDKsDirectory, + fileSystem: self.fileSystem, + observabilityScope: observabilityScope, + outputHandler: { print($0) } + ) + let configurationStore = try SwiftSDKConfigurationStore( + hostTimeTriple: triple, + swiftSDKBundleStore: bundleStore + ) + let targetTriple = try Triple(self.targetTriple) + + guard let swiftSDK = try configurationStore.readConfiguration( + sdkID: sdkID, + targetTriple: targetTriple + ) else { + throw SwiftSDKError.swiftSDKNotFound( + artifactID: sdkID, + hostTriple: triple, + targetTriple: targetTriple + ) + } + + if self.shouldShowConfiguration { + print(swiftSDK.pathsConfiguration) + return + } + + var configuration = swiftSDK.pathsConfiguration + if self.shouldReset { + if try !configurationStore.resetConfiguration(sdkID: sdkID, targetTriple: targetTriple) { + observabilityScope.emit( + warning: "No configuration for Swift SDK `\(sdkID)`" + ) + } else { + observabilityScope.emit( + info: """ + All configuration properties of Swift SDK `\(sdkID)` for target triple \ + `\(targetTriple)` were successfully reset. + """ + ) + } + } else { + var updatedProperties = [String]() + + let currentWorkingDirectory: AbsolutePath? = fileSystem.currentWorkingDirectory + + if let sdkRootPath { + configuration.sdkRootPath = try AbsolutePath(validating: sdkRootPath, relativeTo: currentWorkingDirectory) + updatedProperties.append(CodingKeys.sdkRootPath.stringValue) + } + + if let swiftResourcesPath { + configuration.swiftResourcesPath = + try AbsolutePath(validating: swiftResourcesPath, relativeTo: currentWorkingDirectory) + updatedProperties.append(CodingKeys.swiftResourcesPath.stringValue) + } + + if let swiftStaticResourcesPath { + configuration.swiftResourcesPath = + try AbsolutePath(validating: swiftStaticResourcesPath, relativeTo: currentWorkingDirectory) + updatedProperties.append(CodingKeys.swiftStaticResourcesPath.stringValue) + } + + if !includeSearchPath.isEmpty { + configuration.includeSearchPaths = + try includeSearchPath.map { try AbsolutePath(validating: $0, relativeTo: currentWorkingDirectory) } + updatedProperties.append(CodingKeys.includeSearchPath.stringValue) + } + + if !librarySearchPath.isEmpty { + configuration.librarySearchPaths = + try librarySearchPath.map { try AbsolutePath(validating: $0, relativeTo: currentWorkingDirectory) } + updatedProperties.append(CodingKeys.librarySearchPath.stringValue) + } + + if !toolsetPath.isEmpty { + configuration.toolsetPaths = + try toolsetPath.map { try AbsolutePath(validating: $0, relativeTo: currentWorkingDirectory) } + updatedProperties.append(CodingKeys.toolsetPath.stringValue) + } + + guard !updatedProperties.isEmpty else { + observabilityScope.emit( + error: """ + No properties of Swift SDK `\(sdkID)` for target triple `\(targetTriple)` were updated \ + since none were specified. Pass `--help` flag to see the list of all available properties. + """ + ) + return + } + + var swiftSDK = swiftSDK + swiftSDK.pathsConfiguration = configuration + try configurationStore.updateConfiguration(sdkID: sdkID, swiftSDK: swiftSDK) + + observabilityScope.emit( + info: """ + These properties of Swift SDK `\(sdkID)` for target triple \ + `\(targetTriple)` were successfully updated: \(updatedProperties.joined(separator: ", ")). + """ + ) + } + + if observabilityScope.errorsReported { + throw ExitCode.failure + } + } catch { + commandError = error + } + + // wait for all observability items to process + observabilityHandler.wait(timeout: .now() + 5) + + if let commandError { + throw commandError + } + } +} + +extension AbsolutePath { + fileprivate init(validating string: String, relativeTo basePath: AbsolutePath?) throws { + if let basePath { + try self.init(validating: string, relativeTo: basePath) + } else { + try self.init(validating: string) + } + } +} diff --git a/Sources/SwiftSDKCommand/InstallSwiftSDK.swift b/Sources/SwiftSDKCommand/InstallSwiftSDK.swift index 16162c6a9b1..354b97b00d2 100644 --- a/Sources/SwiftSDKCommand/InstallSwiftSDK.swift +++ b/Sources/SwiftSDKCommand/InstallSwiftSDK.swift @@ -18,8 +18,8 @@ import PackageModel import var TSCBasic.stdoutStream -package struct InstallSwiftSDK: SwiftSDKSubcommand { - package static let configuration = CommandConfiguration( +struct InstallSwiftSDK: SwiftSDKSubcommand { + static let configuration = CommandConfiguration( commandName: "install", abstract: """ Installs a given Swift SDK bundle to a location discoverable by SwiftPM. If the artifact bundle \ @@ -33,8 +33,6 @@ package struct InstallSwiftSDK: SwiftSDKSubcommand { @Argument(help: "A local filesystem path or a URL of a Swift SDK bundle to install.") var bundlePathOrURL: String - package init() {} - func run( hostTriple: Triple, _ swiftSDKsDirectory: AbsolutePath, diff --git a/Sources/SwiftSDKCommand/ListSwiftSDKs.swift b/Sources/SwiftSDKCommand/ListSwiftSDKs.swift index 062b43b9e75..a6f257d0289 100644 --- a/Sources/SwiftSDKCommand/ListSwiftSDKs.swift +++ b/Sources/SwiftSDKCommand/ListSwiftSDKs.swift @@ -16,8 +16,8 @@ import CoreCommands import PackageModel import SPMBuildCore -package struct ListSwiftSDKs: SwiftSDKSubcommand { - package static let configuration = CommandConfiguration( +struct ListSwiftSDKs: SwiftSDKSubcommand { + static let configuration = CommandConfiguration( commandName: "list", abstract: """ @@ -28,8 +28,6 @@ package struct ListSwiftSDKs: SwiftSDKSubcommand { @OptionGroup() var locations: LocationOptions - package init() {} - func run( hostTriple: Triple, _ swiftSDKsDirectory: AbsolutePath, diff --git a/Sources/SwiftSDKCommand/RemoveSwiftSDK.swift b/Sources/SwiftSDKCommand/RemoveSwiftSDK.swift index 5e2ef7a1ff1..d74e0022cf0 100644 --- a/Sources/SwiftSDKCommand/RemoveSwiftSDK.swift +++ b/Sources/SwiftSDKCommand/RemoveSwiftSDK.swift @@ -15,8 +15,8 @@ import Basics import CoreCommands import PackageModel -package struct RemoveSwiftSDK: SwiftSDKSubcommand { - package static let configuration = CommandConfiguration( +struct RemoveSwiftSDK: SwiftSDKSubcommand { + static let configuration = CommandConfiguration( commandName: "remove", abstract: """ Removes a previously installed Swift SDK bundle from the filesystem. @@ -29,8 +29,6 @@ package struct RemoveSwiftSDK: SwiftSDKSubcommand { @Argument(help: "Name of the Swift SDK bundle or ID of the Swift SDK to remove from the filesystem.") var sdkIDOrBundleName: String - package init() {} - func run( hostTriple: Triple, _ swiftSDKsDirectory: AbsolutePath, diff --git a/Sources/SwiftSDKCommand/SwiftSDKCommand.swift b/Sources/SwiftSDKCommand/SwiftSDKCommand.swift index e39740d4796..e167cc548ac 100644 --- a/Sources/SwiftSDKCommand/SwiftSDKCommand.swift +++ b/Sources/SwiftSDKCommand/SwiftSDKCommand.swift @@ -21,6 +21,7 @@ package struct SwiftSDKCommand: AsyncParsableCommand { version: SwiftVersion.current.completeDisplayString, subcommands: [ ConfigureSwiftSDK.self, + DeprecatedSwiftSDKConfigurationCommand.self, InstallSwiftSDK.self, ListSwiftSDKs.self, RemoveSwiftSDK.self, diff --git a/Sources/SwiftSDKCommand/SwiftSDKSubcommand.swift b/Sources/SwiftSDKCommand/SwiftSDKSubcommand.swift index 0f93f6b2274..1ec68898a6e 100644 --- a/Sources/SwiftSDKCommand/SwiftSDKSubcommand.swift +++ b/Sources/SwiftSDKCommand/SwiftSDKSubcommand.swift @@ -54,7 +54,7 @@ extension SwiftSDKSubcommand { return swiftSDKsDirectory } - package func run() async throws { + func run() async throws { let observabilityHandler = SwiftCommandObservabilityHandler(outputStream: stdoutStream, logLevel: .info) let observabilitySystem = ObservabilitySystem(observabilityHandler) let observabilityScope = observabilitySystem.topScope diff --git a/Sources/swift-experimental-sdk/Entrypoint.swift b/Sources/swift-experimental-sdk/Entrypoint.swift index 7e9aa3cc642..392d07a4105 100644 --- a/Sources/swift-experimental-sdk/Entrypoint.swift +++ b/Sources/swift-experimental-sdk/Entrypoint.swift @@ -16,7 +16,7 @@ import Foundation @main struct Entrypoint { static func main() async { - fputs("warning: `swift experimental-sdk` command is deprecated and will be removed in a future version of SwiftPM. Use `swift sdk` instead.", stderr) + fputs("warning: `swift experimental-sdk` command is deprecated and will be removed in a future version of SwiftPM. Use `swift sdk` instead.\n", stderr) await SwiftSDKCommand.main() } } diff --git a/Sources/swift-package-manager/SwiftPM.swift b/Sources/swift-package-manager/SwiftPM.swift index 5440b72b79f..3bc47218db5 100644 --- a/Sources/swift-package-manager/SwiftPM.swift +++ b/Sources/swift-package-manager/SwiftPM.swift @@ -42,7 +42,7 @@ struct SwiftPM { case "swift-build": await SwiftBuildCommand.main() case "swift-experimental-sdk": - fputs("warning: `swift experimental-sdk` command is deprecated and will be removed in a future version of SwiftPM. Use `swift sdk` instead.", stderr) + fputs("warning: `swift experimental-sdk` command is deprecated and will be removed in a future version of SwiftPM. Use `swift sdk` instead.\n", stderr) fallthrough case "swift-sdk": await SwiftSDKCommand.main() diff --git a/Tests/CommandsTests/SDKCommandTests.swift b/Tests/CommandsTests/SDKCommandTests.swift deleted file mode 100644 index 55619494954..00000000000 --- a/Tests/CommandsTests/SDKCommandTests.swift +++ /dev/null @@ -1,121 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift open source project -// -// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See http://swift.org/LICENSE.txt for license information -// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import Basics -import Commands -import SPMTestSupport -import XCTest - -import class TSCBasic.Process -import enum TSCBasic.ProcessEnv - -private let deprecationWarning = "warning: `swift experimental-sdk` command is deprecated and will be removed in a future version of SwiftPM. Use `swift sdk` instead." - -final class SDKCommandTests: CommandsTestCase { - func testUsage() throws { - for command in [SwiftPM.sdk, SwiftPM.experimentalSDK] { - let stdout = try command.execute(["-help"]).stdout - XCTAssert(stdout.contains("USAGE: swift sdk ") || stdout.contains("USAGE: swift sdk []"), "got stdout:\n" + stdout) - } - } - - func testVersion() throws { - for command in [SwiftPM.sdk, SwiftPM.experimentalSDK] { - let stdout = try command.execute(["--version"]).stdout - XCTAssert(stdout.contains("Swift Package Manager"), "got stdout:\n" + stdout) - } - } - - func testInstallSDK() throws { - for command in [SwiftPM.sdk, SwiftPM.experimentalSDK] { - try fixture(name: "SwiftSDKs") { fixturePath in - for bundle in ["test-sdk.artifactbundle.tar.gz", "test-sdk.artifactbundle.zip"] { - var (stdout, stderr) = try command.execute( - [ - "install", - "--swift-sdks-path", fixturePath.pathString, - fixturePath.appending(bundle).pathString - ] - ) - - if command == .experimentalSDK { - XCTAssertMatch(stderr, .contains(deprecationWarning)) - XCTAssertNoMatch(stdout, .contains(deprecationWarning)) - } - - // We only expect tool's output on the stdout stream. - XCTAssertMatch( - stdout, - .contains("\(bundle)` successfully installed as test-sdk.artifactbundle.") - ) - - (stdout, stderr) = try command.execute( - ["list", "--swift-sdks-path", fixturePath.pathString]) - - if command == .experimentalSDK { - XCTAssertMatch(stderr, .contains(deprecationWarning)) - XCTAssertNoMatch(stdout, .contains(deprecationWarning)) - } - - // We only expect tool's output on the stdout stream. - XCTAssertMatch(stdout, .contains("test-artifact")) - - XCTAssertThrowsError(try command.execute( - [ - "install", - "--swift-sdks-path", fixturePath.pathString, - fixturePath.appending(bundle).pathString - ] - )) { error in - guard case SwiftPMError.executionFailure(_, _, let stderr) = error else { - XCTFail() - return - } - - XCTAssertTrue( - stderr.contains( - "Error: Swift SDK bundle with name `test-sdk.artifactbundle` is already installed. Can't install a new bundle with the same name." - ), - "got stderr: \(stderr)" - ) - } - - if command == .experimentalSDK { - XCTAssertMatch(stderr, .contains(deprecationWarning)) - } - - (stdout, stderr) = try command.execute( - ["remove", "--swift-sdks-path", fixturePath.pathString, "test-artifact"]) - - if command == .experimentalSDK { - XCTAssertMatch(stderr, .contains(deprecationWarning)) - XCTAssertNoMatch(stdout, .contains(deprecationWarning)) - } - - // We only expect tool's output on the stdout stream. - XCTAssertMatch(stdout, .contains("test-sdk.artifactbundle` was successfully removed from the file system.")) - - (stdout, stderr) = try command.execute( - ["list", "--swift-sdks-path", fixturePath.pathString]) - - if command == .experimentalSDK { - XCTAssertMatch(stderr, .contains(deprecationWarning)) - XCTAssertNoMatch(stdout, .contains(deprecationWarning)) - } - - // We only expect tool's output on the stdout stream. - XCTAssertNoMatch(stdout, .contains("test-artifact")) - } - } - } - } -} diff --git a/Tests/CommandsTests/SwiftSDKCommandTests.swift b/Tests/CommandsTests/SwiftSDKCommandTests.swift new file mode 100644 index 00000000000..b9e80667059 --- /dev/null +++ b/Tests/CommandsTests/SwiftSDKCommandTests.swift @@ -0,0 +1,265 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Basics +import Commands +import SPMTestSupport +import XCTest + +import class TSCBasic.Process +import enum TSCBasic.ProcessEnv + +private let sdkCommandDeprecationWarning = """ + warning: `swift experimental-sdk` command is deprecated and will be removed in a future version of SwiftPM. Use \ + `swift sdk` instead. + + """ + + +final class SwiftSDKCommandTests: CommandsTestCase { + func testUsage() throws { + for command in [SwiftPM.sdk, SwiftPM.experimentalSDK] { + let stdout = try command.execute(["-help"]).stdout + XCTAssert(stdout.contains("USAGE: swift sdk ") || stdout.contains("USAGE: swift sdk []"), "got stdout:\n" + stdout) + } + } + + func testVersion() throws { + for command in [SwiftPM.sdk, SwiftPM.experimentalSDK] { + let stdout = try command.execute(["--version"]).stdout + XCTAssert(stdout.contains("Swift Package Manager"), "got stdout:\n" + stdout) + } + } + + func testInstallSubcommand() throws { + for command in [SwiftPM.sdk, SwiftPM.experimentalSDK] { + try fixture(name: "SwiftSDKs") { fixturePath in + for bundle in ["test-sdk.artifactbundle.tar.gz", "test-sdk.artifactbundle.zip"] { + var (stdout, stderr) = try command.execute( + [ + "install", + "--swift-sdks-path", fixturePath.pathString, + fixturePath.appending(bundle).pathString + ] + ) + + if command == .experimentalSDK { + XCTAssertMatch(stderr, .contains(sdkCommandDeprecationWarning)) + XCTAssertNoMatch(stdout, .contains(sdkCommandDeprecationWarning)) + } + + // We only expect tool's output on the stdout stream. + XCTAssertMatch( + stdout, + .contains("\(bundle)` successfully installed as test-sdk.artifactbundle.") + ) + + (stdout, stderr) = try command.execute( + ["list", "--swift-sdks-path", fixturePath.pathString]) + + if command == .experimentalSDK { + XCTAssertMatch(stderr, .contains(sdkCommandDeprecationWarning)) + XCTAssertNoMatch(stdout, .contains(sdkCommandDeprecationWarning)) + } + + // We only expect tool's output on the stdout stream. + XCTAssertMatch(stdout, .contains("test-artifact")) + + XCTAssertThrowsError(try command.execute( + [ + "install", + "--swift-sdks-path", fixturePath.pathString, + fixturePath.appending(bundle).pathString + ] + )) { error in + guard case SwiftPMError.executionFailure(_, _, let stderr) = error else { + XCTFail() + return + } + + XCTAssertTrue( + stderr.contains( + "Error: Swift SDK bundle with name `test-sdk.artifactbundle` is already installed. Can't install a new bundle with the same name." + ), + "got stderr: \(stderr)" + ) + } + + if command == .experimentalSDK { + XCTAssertMatch(stderr, .contains(sdkCommandDeprecationWarning)) + } + + (stdout, stderr) = try command.execute( + ["remove", "--swift-sdks-path", fixturePath.pathString, "test-artifact"]) + + if command == .experimentalSDK { + XCTAssertMatch(stderr, .contains(sdkCommandDeprecationWarning)) + XCTAssertNoMatch(stdout, .contains(sdkCommandDeprecationWarning)) + } + + // We only expect tool's output on the stdout stream. + XCTAssertMatch(stdout, .contains("test-sdk.artifactbundle` was successfully removed from the file system.")) + + (stdout, stderr) = try command.execute( + ["list", "--swift-sdks-path", fixturePath.pathString]) + + if command == .experimentalSDK { + XCTAssertMatch(stderr, .contains(sdkCommandDeprecationWarning)) + XCTAssertNoMatch(stdout, .contains(sdkCommandDeprecationWarning)) + } + + // We only expect tool's output on the stdout stream. + XCTAssertNoMatch(stdout, .contains("test-artifact")) + } + } + } + } + + func testConfigureSubcommand() throws { + let deprecationWarning = """ + warning: `swift sdk configuration` command is deprecated and will be removed in a future version of \ + SwiftPM. Use `swift sdk configure` instead. + + """ + + for command in [SwiftPM.sdk, SwiftPM.experimentalSDK] { + try fixture(name: "SwiftSDKs") { fixturePath in + let bundle = "test-sdk.artifactbundle.zip" + + var (stdout, stderr) = try command.execute([ + "install", + "--swift-sdks-path", fixturePath.pathString, + fixturePath.appending(bundle).pathString + ]) + + // We only expect tool's output on the stdout stream. + XCTAssertMatch( + stdout, + .contains("\(bundle)` successfully installed as test-sdk.artifactbundle.") + ) + + let deprecatedShowSubcommand = ["configuration", "show"] + + for showSubcommand in [deprecatedShowSubcommand, ["configure", "--show-configuration"]] { + let invocation = showSubcommand + [ + "--swift-sdks-path", fixturePath.pathString, + "test-artifact", + "aarch64-unknown-linux-gnu", + ] + (stdout, stderr) = try command.execute(invocation) + + if command == .experimentalSDK { + XCTAssertMatch(stderr, .contains(sdkCommandDeprecationWarning)) + } + + if showSubcommand == deprecatedShowSubcommand { + XCTAssertMatch(stderr, .contains(deprecationWarning)) + } + + let sdkSubpath = "test-sdk.artifactbundle/sdk/sdk" + + XCTAssertEqual(stdout, + """ + sdkRootPath: \(fixturePath.pathString)/\(sdkSubpath) + swiftResourcesPath: not set + swiftStaticResourcesPath: not set + includeSearchPaths: not set + librarySearchPaths: not set + toolsetPaths: not set + + """, + invocation.joined(separator: " ") + ) + + let deprecatedSetSubcommand = ["configuration", "set"] + let deprecatedResetSubcommand = ["configuration", "reset"] + for setSubcommand in [deprecatedSetSubcommand, ["configure"]] { + for resetSubcommand in [deprecatedResetSubcommand, ["configure", "--reset"]] { + var invocation = setSubcommand + [ + "--swift-resources-path", fixturePath.appending("foo").pathString, + "--swift-sdks-path", fixturePath.pathString, + "test-artifact", + "aarch64-unknown-linux-gnu", + ] + (stdout, stderr) = try command.execute(invocation) + + XCTAssertEqual(stdout, """ + info: These properties of Swift SDK `test-artifact` for target triple `aarch64-unknown-linux-gnu` \ + were successfully updated: swiftResourcesPath. + + """, + invocation.joined(separator: " ") + ) + + if command == .experimentalSDK { + XCTAssertMatch(stderr, .contains(sdkCommandDeprecationWarning)) + } + + if setSubcommand == deprecatedSetSubcommand { + XCTAssertMatch(stderr, .contains(deprecationWarning)) + } + + invocation = showSubcommand + [ + "--swift-sdks-path", fixturePath.pathString, + "test-artifact", + "aarch64-unknown-linux-gnu", + ] + (stdout, stderr) = try command.execute(invocation) + + XCTAssertEqual(stdout, + """ + sdkRootPath: \(fixturePath.pathString)/\(sdkSubpath) + swiftResourcesPath: \(fixturePath.pathString)/foo + swiftStaticResourcesPath: not set + includeSearchPaths: not set + librarySearchPaths: not set + toolsetPaths: not set + + """, + invocation.joined(separator: " ") + ) + + invocation = resetSubcommand + [ + "--swift-sdks-path", fixturePath.pathString, + "test-artifact", + "aarch64-unknown-linux-gnu", + ] + (stdout, stderr) = try command.execute(invocation) + + if command == .experimentalSDK { + XCTAssertMatch(stderr, .contains(sdkCommandDeprecationWarning)) + } + + if resetSubcommand == deprecatedResetSubcommand { + XCTAssertMatch(stderr, .contains(deprecationWarning)) + } + + XCTAssertEqual(stdout, + """ + info: All configuration properties of Swift SDK `test-artifact` for target triple `aarch64-unknown-linux-gnu` were successfully reset. + + """, + invocation.joined(separator: " ") + ) + } + } + } + + (stdout, stderr) = try command.execute( + ["remove", "--swift-sdks-path", fixturePath.pathString, "test-artifact"]) + + // We only expect tool's output on the stdout stream. + XCTAssertMatch(stdout, .contains("test-sdk.artifactbundle` was successfully removed from the file system.")) + } + } + } +} From 86984752a0a2e69e0799c2ed50c91deb2589a62e Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 9 May 2024 16:53:14 +0100 Subject: [PATCH 140/159] Mark `buildSettingsDescription` as `@_spi` in `Target.swift` (#7542) rdar://127782783 --- Sources/Build/BuildDescription/ProductBuildDescription.swift | 1 + Sources/PackageModel/Target/Target.swift | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Sources/Build/BuildDescription/ProductBuildDescription.swift b/Sources/Build/BuildDescription/ProductBuildDescription.swift index 289c48dec48..1cb1018bc95 100644 --- a/Sources/Build/BuildDescription/ProductBuildDescription.swift +++ b/Sources/Build/BuildDescription/ProductBuildDescription.swift @@ -13,6 +13,7 @@ import Basics import PackageGraph +@_spi(SwiftPMInternal) import PackageModel import OrderedCollections diff --git a/Sources/PackageModel/Target/Target.swift b/Sources/PackageModel/Target/Target.swift index 1074e353c87..95208121df6 100644 --- a/Sources/PackageModel/Target/Target.swift +++ b/Sources/PackageModel/Target/Target.swift @@ -235,7 +235,8 @@ public class Target: PolymorphicCodableProtocol { /// The build settings assignments of this target. public let buildSettings: BuildSettings.AssignmentTable - package let buildSettingsDescription: [TargetBuildSettingDescription.Setting] + @_spi(SwiftPMInternal) + public let buildSettingsDescription: [TargetBuildSettingDescription.Setting] /// The usages of package plugins by this target. public let pluginUsages: [PluginUsage] From 0d670e7405cd394c500f2132d3c14ae2c1ca2690 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Thu, 9 May 2024 12:02:09 -0700 Subject: [PATCH 141/159] Add API for background preparation in SourceKit-LSP (#7540) - Add an API to get the name of a target from SourceKit-LSP - We will need this API to prepare a target for indexing. - Add an API to get all the targets in a build description / modules graph in a topological order - We need this for background preparation for indexing so that we can prepare and index lower-level targets first. --- Sources/PackageGraph/ModulesGraph.swift | 8 ++++++++ .../SourceKitLSPAPI/BuildDescription.swift | 19 +++++++++++++++++++ .../PluginTargetBuildDescription.swift | 4 ++++ 3 files changed, 31 insertions(+) diff --git a/Sources/PackageGraph/ModulesGraph.swift b/Sources/PackageGraph/ModulesGraph.swift index 4c65a1ba9e9..6b0f9684d9b 100644 --- a/Sources/PackageGraph/ModulesGraph.swift +++ b/Sources/PackageGraph/ModulesGraph.swift @@ -101,6 +101,14 @@ public struct ModulesGraph { /// Returns all the targets in the graph, regardless if they are reachable from the root targets or not. public private(set) var allTargets: IdentifiableSet + /// Returns all targets within the module graph in topological order, starting with low-level targets (that have no + /// dependencies). + package var allTargetsInTopologicalOrder: [ResolvedModule] { + get throws { + try topologicalSort(Array(allTargets)) { $0.dependencies.compactMap { $0.target } }.reversed() + } + } + /// Returns all the products in the graph, regardless if they are reachable from the root targets or not. public private(set) var allProducts: IdentifiableSet diff --git a/Sources/SourceKitLSPAPI/BuildDescription.swift b/Sources/SourceKitLSPAPI/BuildDescription.swift index af012d12802..ddb2030c912 100644 --- a/Sources/SourceKitLSPAPI/BuildDescription.swift +++ b/Sources/SourceKitLSPAPI/BuildDescription.swift @@ -27,6 +27,9 @@ import struct PackageGraph.ModulesGraph public protocol BuildTarget { var sources: [URL] { get } + /// The name of the target. It should be possible to build a target by passing this name to `swift build --target` + var name: String { get } + /// Whether the target is part of the root package that the user opened or if it's part of a package dependency. var isPartOfRootPackage: Bool { get } @@ -46,6 +49,10 @@ private struct WrappedClangTargetBuildDescription: BuildTarget { return (try? description.compilePaths().map { URL(fileURLWithPath: $0.source.pathString) }) ?? [] } + public var name: String { + return description.clangTarget.name + } + public func compileArguments(for fileURL: URL) throws -> [String] { let filePath = try resolveSymlinks(try AbsolutePath(validating: fileURL.path)) let commandLine = try description.emitCommandLine(for: filePath) @@ -63,6 +70,10 @@ private struct WrappedSwiftTargetBuildDescription: BuildTarget { self.isPartOfRootPackage = isPartOfRootPackage } + public var name: String { + return description.target.name + } + var sources: [URL] { return description.sources.map { URL(fileURLWithPath: $0.pathString) } } @@ -110,4 +121,12 @@ public struct BuildDescription { return nil } } + + /// Returns all targets within the module graph in topological order, starting with low-level targets (that have no + /// dependencies). + public func allTargetsInTopologicalOrder(in modulesGraph: ModulesGraph) throws -> [BuildTarget] { + try modulesGraph.allTargetsInTopologicalOrder.compactMap { + getBuildTarget(for: $0, in: modulesGraph) + } + } } diff --git a/Sources/SourceKitLSPAPI/PluginTargetBuildDescription.swift b/Sources/SourceKitLSPAPI/PluginTargetBuildDescription.swift index 8e6a147cc3e..5c6ab83197b 100644 --- a/Sources/SourceKitLSPAPI/PluginTargetBuildDescription.swift +++ b/Sources/SourceKitLSPAPI/PluginTargetBuildDescription.swift @@ -34,6 +34,10 @@ struct PluginTargetBuildDescription: BuildTarget { return target.sources.paths.map { URL(fileURLWithPath: $0.pathString) } } + var name: String { + return target.name + } + func compileArguments(for fileURL: URL) throws -> [String] { // FIXME: This is very odd and we should clean this up by merging `ManifestLoader` and `DefaultPluginScriptRunner` again. let loader = ManifestLoader(toolchain: try UserToolchain(swiftSDK: .hostSwiftSDK())) From 213844c3f0326778cc9699e1a7ac4535d86695c5 Mon Sep 17 00:00:00 2001 From: Ben Barham Date: Wed, 17 Apr 2024 21:11:32 -0700 Subject: [PATCH 142/159] Update -swift-version to default to 6 for swift-tools-version 6 We missed updating this when adding 6.0. The change itself is extremely simple, with the bulk being updates to our tests so they don't fail on old compiler versions (where language version "6" is unknown). Also removes disabling the concurrency and string processing imports from manifest loading, as this was causing the `package` global to not be main actor isolated by default and thus error in Swift 6. They seemed to have been disabled to avoid erroring in SDKs that didn't have them, but that is definitely not an issue any longer. --- .../Plugins/DependentPlugins/Package.swift | 7 +-- .../Sources/MySourceGenBuildTool/main.swift | 2 +- .../Sources/MySourceGenBuildTool/main.swift | 2 +- .../Sources/MySourceGenBuildTool/main.swift | 2 +- .../Sources/PluginExecutable/main.swift | 2 +- .../Tests/IntegrationTests/BasicTests.swift | 25 ++++++---- .../Tests/IntegrationTests/SwiftPMTests.swift | 3 ++ .../SwiftTargetBuildDescription.swift | 5 ++ Sources/CoreCommands/SwiftCommandState.swift | 17 ------- Sources/PackageModel/ToolsVersion.swift | 7 +-- Sources/SPMTestSupport/XCTSkipHelpers.swift | 29 ++++++++++++ Tests/BuildTests/BuildPlanTests.swift | 47 +++++++++++++++++++ .../BuildTests/BuildSystemDelegateTests.swift | 3 +- Tests/FunctionalTests/PluginTests.swift | 12 +++-- Tests/FunctionalTests/ResourcesTests.swift | 5 +- .../ManifestLoaderCacheTests.swift | 15 +++++- .../PD_6_0_LoadingTests.swift | 10 +++- .../PackageBuilderTests.swift | 2 +- .../PackageModelTests/ToolsVersionTests.swift | 6 ++- Tests/WorkspaceTests/InitTests.swift | 29 ++++++++---- .../ManifestSourceGenerationTests.swift | 6 +-- Tests/WorkspaceTests/WorkspaceTests.swift | 8 ++-- 22 files changed, 182 insertions(+), 62 deletions(-) create mode 100644 Sources/SPMTestSupport/XCTSkipHelpers.swift diff --git a/Fixtures/Miscellaneous/Plugins/DependentPlugins/Package.swift b/Fixtures/Miscellaneous/Plugins/DependentPlugins/Package.swift index 9d77aa3bd15..19db113db4f 100644 --- a/Fixtures/Miscellaneous/Plugins/DependentPlugins/Package.swift +++ b/Fixtures/Miscellaneous/Plugins/DependentPlugins/Package.swift @@ -10,7 +10,7 @@ let package = Package( targets: [ .executableTarget(name: "MyExecutable"), .executableTarget(name: "MyExecutable2"), - + .plugin( name: "MyPlugin", capability: .buildTool(), @@ -18,7 +18,7 @@ let package = Package( "MyExecutable" ] ), - + .plugin( name: "MyPlugin2", capability: .buildTool(), @@ -34,5 +34,6 @@ let package = Package( "MyPlugin2", ] ), - ] + ], + swiftLanguageVersions: [.v5] ) diff --git a/Fixtures/Miscellaneous/Plugins/MyBuildToolPluginDependencies/Sources/MySourceGenBuildTool/main.swift b/Fixtures/Miscellaneous/Plugins/MyBuildToolPluginDependencies/Sources/MySourceGenBuildTool/main.swift index 94f7766628c..bedca50b459 100644 --- a/Fixtures/Miscellaneous/Plugins/MyBuildToolPluginDependencies/Sources/MySourceGenBuildTool/main.swift +++ b/Fixtures/Miscellaneous/Plugins/MyBuildToolPluginDependencies/Sources/MySourceGenBuildTool/main.swift @@ -13,6 +13,6 @@ let variableName = URL(fileURLWithPath: inputFile).deletingPathExtension().lastP let inputData = FileManager.default.contents(atPath: inputFile) ?? Data() let dataAsHex = inputData.map { String(format: "%02hhx", $0) }.joined() -let outputString = "public var \(variableName) = \(dataAsHex.quotedForSourceCode)\n" +let outputString = "public let \(variableName) = \(dataAsHex.quotedForSourceCode)\n" let outputData = outputString.data(using: .utf8) FileManager.default.createFile(atPath: outputFile, contents: outputData) diff --git a/Fixtures/Miscellaneous/Plugins/MySourceGenPlugin/Sources/MySourceGenBuildTool/main.swift b/Fixtures/Miscellaneous/Plugins/MySourceGenPlugin/Sources/MySourceGenBuildTool/main.swift index 94f7766628c..bedca50b459 100644 --- a/Fixtures/Miscellaneous/Plugins/MySourceGenPlugin/Sources/MySourceGenBuildTool/main.swift +++ b/Fixtures/Miscellaneous/Plugins/MySourceGenPlugin/Sources/MySourceGenBuildTool/main.swift @@ -13,6 +13,6 @@ let variableName = URL(fileURLWithPath: inputFile).deletingPathExtension().lastP let inputData = FileManager.default.contents(atPath: inputFile) ?? Data() let dataAsHex = inputData.map { String(format: "%02hhx", $0) }.joined() -let outputString = "public var \(variableName) = \(dataAsHex.quotedForSourceCode)\n" +let outputString = "public let \(variableName) = \(dataAsHex.quotedForSourceCode)\n" let outputData = outputString.data(using: .utf8) FileManager.default.createFile(atPath: outputFile, contents: outputData) diff --git a/Fixtures/Miscellaneous/Plugins/MySourceGenPluginUsingURLBasedAPI/Sources/MySourceGenBuildTool/main.swift b/Fixtures/Miscellaneous/Plugins/MySourceGenPluginUsingURLBasedAPI/Sources/MySourceGenBuildTool/main.swift index 94f7766628c..bedca50b459 100644 --- a/Fixtures/Miscellaneous/Plugins/MySourceGenPluginUsingURLBasedAPI/Sources/MySourceGenBuildTool/main.swift +++ b/Fixtures/Miscellaneous/Plugins/MySourceGenPluginUsingURLBasedAPI/Sources/MySourceGenBuildTool/main.swift @@ -13,6 +13,6 @@ let variableName = URL(fileURLWithPath: inputFile).deletingPathExtension().lastP let inputData = FileManager.default.contents(atPath: inputFile) ?? Data() let dataAsHex = inputData.map { String(format: "%02hhx", $0) }.joined() -let outputString = "public var \(variableName) = \(dataAsHex.quotedForSourceCode)\n" +let outputString = "public let \(variableName) = \(dataAsHex.quotedForSourceCode)\n" let outputData = outputString.data(using: .utf8) FileManager.default.createFile(atPath: outputFile, contents: outputData) diff --git a/Fixtures/Miscellaneous/Plugins/PluginWithInternalExecutable/Sources/PluginExecutable/main.swift b/Fixtures/Miscellaneous/Plugins/PluginWithInternalExecutable/Sources/PluginExecutable/main.swift index 034d9732cd0..6a8b8f23020 100644 --- a/Fixtures/Miscellaneous/Plugins/PluginWithInternalExecutable/Sources/PluginExecutable/main.swift +++ b/Fixtures/Miscellaneous/Plugins/PluginWithInternalExecutable/Sources/PluginExecutable/main.swift @@ -12,6 +12,6 @@ let variableName = URL(fileURLWithPath: inputFile).deletingPathExtension().lastP let inputData = FileManager.default.contents(atPath: inputFile) ?? Data() let dataAsHex = inputData.map { String(format: "%02hhx", $0) }.joined() -let outputString = "public var \(variableName) = \(dataAsHex.quotedForSourceCode)\n" +let outputString = "public let \(variableName) = \(dataAsHex.quotedForSourceCode)\n" let outputData = outputString.data(using: .utf8) FileManager.default.createFile(atPath: outputFile, contents: outputData) diff --git a/IntegrationTests/Tests/IntegrationTests/BasicTests.swift b/IntegrationTests/Tests/IntegrationTests/BasicTests.swift index 8cd4ddab56f..032d14fc0b3 100644 --- a/IntegrationTests/Tests/IntegrationTests/BasicTests.swift +++ b/IntegrationTests/Tests/IntegrationTests/BasicTests.swift @@ -19,6 +19,7 @@ final class BasicTests: XCTestCase { func testExamplePackageDealer() throws { try XCTSkipIf(isSelfHosted, "These packages don't use the latest runtime library, which doesn't work with self-hosted builds.") + try skipUnlessAtLeastSwift6() try withTemporaryDirectory { tempDir in let packagePath = tempDir.appending(component: "dealer") @@ -93,9 +94,7 @@ final class BasicTests: XCTestCase { } func testSwiftPackageInitExec() throws { - #if swift(<5.5) - try XCTSkipIf(true, "skipping because host compiler doesn't support '-entry-point-function-name'") - #endif + try skipUnlessAtLeastSwift6() try withTemporaryDirectory { tempDir in // Create a new package with an executable target. @@ -122,9 +121,7 @@ final class BasicTests: XCTestCase { } func testSwiftPackageInitExecTests() throws { - #if swift(<5.5) - try XCTSkipIf(true, "skipping because host compiler doesn't support '-entry-point-function-name'") - #endif + try skipUnlessAtLeastSwift6() try XCTSkip("FIXME: swift-test invocations are timing out in Xcode and self-hosted CI") @@ -149,6 +146,8 @@ final class BasicTests: XCTestCase { } func testSwiftPackageInitLib() throws { + try skipUnlessAtLeastSwift6() + try withTemporaryDirectory { tempDir in // Create a new package with an executable target. let packagePath = tempDir.appending(component: "Project") @@ -167,6 +166,8 @@ final class BasicTests: XCTestCase { } func testSwiftPackageLibsTests() throws { + try skipUnlessAtLeastSwift6() + try XCTSkip("FIXME: swift-test invocations are timing out in Xcode and self-hosted CI") try withTemporaryDirectory { tempDir in @@ -225,9 +226,7 @@ final class BasicTests: XCTestCase { } func testSwiftRun() throws { - #if swift(<5.5) - try XCTSkipIf(true, "skipping because host compiler doesn't support '-entry-point-function-name'") - #endif + try skipUnlessAtLeastSwift6() try withTemporaryDirectory { tempDir in let packagePath = tempDir.appending(component: "secho") @@ -256,6 +255,8 @@ final class BasicTests: XCTestCase { } func testSwiftTest() throws { + try skipUnlessAtLeastSwift6() + try XCTSkip("FIXME: swift-test invocations are timing out in Xcode and self-hosted CI") try withTemporaryDirectory { tempDir in @@ -377,3 +378,9 @@ private extension Character { } } } + +private func skipUnlessAtLeastSwift6() throws { + #if compiler(<6.0) + try XCTSkipIf(true, "Skipping because test requires at least Swift 6.0") + #endif +} diff --git a/IntegrationTests/Tests/IntegrationTests/SwiftPMTests.swift b/IntegrationTests/Tests/IntegrationTests/SwiftPMTests.swift index d9fcdccc1bf..fc394df0421 100644 --- a/IntegrationTests/Tests/IntegrationTests/SwiftPMTests.swift +++ b/IntegrationTests/Tests/IntegrationTests/SwiftPMTests.swift @@ -53,6 +53,9 @@ final class SwiftPMTests: XCTestCase { #if !os(macOS) try XCTSkip("Test requires macOS") #endif + #if swift(<6.0) + try XCTSkipIf(true, "Skipping because test requires at least Swift 6.0") + #endif try withTemporaryDirectory { tmpDir in let packagePath = tmpDir.appending(component: "foo") diff --git a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift index 7460c8ce2ef..b1fa8e06039 100644 --- a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift @@ -405,6 +405,11 @@ package final class SwiftTargetBuildDescription { """ import Foundation + #if compiler(>=6.0) + extension Foundation.Bundle: @unchecked @retroactive Sendable {} + #else + extension Foundation.Bundle: @unchecked Sendable {} + #endif extension Foundation.Bundle { static let module: Bundle = { let mainPath = \(mainPathSubstitution) diff --git a/Sources/CoreCommands/SwiftCommandState.swift b/Sources/CoreCommands/SwiftCommandState.swift index 4eb2ad72156..44a61945ad1 100644 --- a/Sources/CoreCommands/SwiftCommandState.swift +++ b/Sources/CoreCommands/SwiftCommandState.swift @@ -881,23 +881,6 @@ package final class SwiftCommandState { } var extraManifestFlags = self.options.build.manifestFlags - // Disable the implicit concurrency import if the compiler in use supports it to avoid warnings if we are building against an older SDK that does not contain a Concurrency module. - if DriverSupport.checkSupportedFrontendFlags( - flags: ["disable-implicit-concurrency-module-import"], - toolchain: try self.toolsBuildParameters.toolchain, - fileSystem: self.fileSystem - ) { - extraManifestFlags += ["-Xfrontend", "-disable-implicit-concurrency-module-import"] - } - // Disable the implicit string processing import if the compiler in use supports it to avoid warnings if we are building against an older SDK that does not contain a StringProcessing module. - if DriverSupport.checkSupportedFrontendFlags( - flags: ["disable-implicit-string-processing-module-import"], - toolchain: try self.toolsBuildParameters.toolchain, - fileSystem: self.fileSystem - ) { - extraManifestFlags += ["-Xfrontend", "-disable-implicit-string-processing-module-import"] - } - if self.logLevel <= .info { extraManifestFlags.append("-v") } diff --git a/Sources/PackageModel/ToolsVersion.swift b/Sources/PackageModel/ToolsVersion.swift index 20920f7424f..571882a2126 100644 --- a/Sources/PackageModel/ToolsVersion.swift +++ b/Sources/PackageModel/ToolsVersion.swift @@ -182,10 +182,11 @@ public struct ToolsVersion: Equatable, Hashable, Codable, Sendable { // Otherwise, use 4.2 return .v4_2 - - default: - // Anything above 4 major version uses version 5. + case 5: return .v5 + default: + // Anything above 5 major version uses version 6. + return .v6 } } } diff --git a/Sources/SPMTestSupport/XCTSkipHelpers.swift b/Sources/SPMTestSupport/XCTSkipHelpers.swift new file mode 100644 index 00000000000..4c96a34ac6c --- /dev/null +++ b/Sources/SPMTestSupport/XCTSkipHelpers.swift @@ -0,0 +1,29 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Basics +import PackageModel +import XCTest + +import class TSCBasic.Process +import struct TSCBasic.StringError + +extension Toolchain { + package func skipUnlessAtLeastSwift6( + file: StaticString = #file, + line: UInt = #line + ) async throws { + #if compiler(<6.0) + try XCTSkipIf(true, "Skipping because test requires at least Swift 6.0") + #endif + } +} diff --git a/Tests/BuildTests/BuildPlanTests.swift b/Tests/BuildTests/BuildPlanTests.swift index 24a4570325d..4b86b47335a 100644 --- a/Tests/BuildTests/BuildPlanTests.swift +++ b/Tests/BuildTests/BuildPlanTests.swift @@ -6368,4 +6368,51 @@ final class BuildPlanTests: XCTestCase { ] ) } + + func testDefaultVersions() throws { + let fs = InMemoryFileSystem(emptyFiles: + "/Pkg/Sources/foo/foo.swift" + ) + + let expectedVersions = [ + ToolsVersion.v4: "4", + ToolsVersion.v4_2: "4.2", + ToolsVersion.v5: "5", + ToolsVersion.v6_0: "6", + ToolsVersion.vNext: "6" + ] + for (toolsVersion, expectedVersionString) in expectedVersions { + let observability = ObservabilitySystem.makeForTesting() + let graph = try loadModulesGraph( + fileSystem: fs, + manifests: [ + Manifest.createRootManifest( + displayName: "Pkg", + path: "/Pkg", + toolsVersion: toolsVersion, + targets: [ + TargetDescription( + name: "foo" + ), + ] + ), + ], + observabilityScope: observability.topScope + ) + + let result = try BuildPlanResult(plan: BuildPlan( + buildParameters: mockBuildParameters(), + graph: graph, + fileSystem: fs, + observabilityScope: observability.topScope + )) + + XCTAssertMatch( + try result.target(for: "foo").swiftTarget().compileArguments(), + [ + "-swift-version", .equal(expectedVersionString) + ] + ) + } + } } diff --git a/Tests/BuildTests/BuildSystemDelegateTests.swift b/Tests/BuildTests/BuildSystemDelegateTests.swift index 4e44a42dafe..d01f4935d06 100644 --- a/Tests/BuildTests/BuildSystemDelegateTests.swift +++ b/Tests/BuildTests/BuildSystemDelegateTests.swift @@ -17,7 +17,8 @@ import XCTest import var TSCBasic.localFileSystem final class BuildSystemDelegateTests: XCTestCase { - func testDoNotFilterLinkerDiagnostics() throws { + func testDoNotFilterLinkerDiagnostics() async throws { + try await UserToolchain.default.skipUnlessAtLeastSwift6() try XCTSkipIf(!UserToolchain.default.supportsSDKDependentTests(), "skipping because test environment doesn't support this test") try fixture(name: "Miscellaneous/DoNotFilterLinkerDiagnostics") { fixturePath in #if !os(macOS) diff --git a/Tests/FunctionalTests/PluginTests.swift b/Tests/FunctionalTests/PluginTests.swift index d96ff4ca8a7..d33a0c17da1 100644 --- a/Tests/FunctionalTests/PluginTests.swift +++ b/Tests/FunctionalTests/PluginTests.swift @@ -178,7 +178,9 @@ final class PluginTests: XCTestCase { } } - func testBuildToolWithoutOutputs() throws { + func testBuildToolWithoutOutputs() async throws { + try await UserToolchain.default.skipUnlessAtLeastSwift6() + // Only run the test if the environment in which we're running actually supports Swift concurrency (which the plugin APIs require). try XCTSkipIf(!UserToolchain.default.supportsSwiftConcurrency(), "skipping because test environment doesn't support concurrency") @@ -1157,7 +1159,9 @@ final class PluginTests: XCTestCase { } } - func testURLBasedPluginAPI() throws { + func testURLBasedPluginAPI() async throws { + try await UserToolchain.default.skipUnlessAtLeastSwift6() + // Only run the test if the environment in which we're running actually supports Swift concurrency (which the plugin APIs require). try XCTSkipIf(!UserToolchain.default.supportsSwiftConcurrency(), "skipping because test environment doesn't support concurrency") @@ -1167,7 +1171,9 @@ final class PluginTests: XCTestCase { } } - func testDependentPlugins() throws { + func testDependentPlugins() async throws { + try await UserToolchain.default.skipUnlessAtLeastSwift6() + try XCTSkipIf(!UserToolchain.default.supportsSwiftConcurrency(), "skipping because test environment doesn't support concurrency") try fixture(name: "Miscellaneous/Plugins/DependentPlugins") { fixturePath in diff --git a/Tests/FunctionalTests/ResourcesTests.swift b/Tests/FunctionalTests/ResourcesTests.swift index 95e6a192bb4..dec955a5b1e 100644 --- a/Tests/FunctionalTests/ResourcesTests.swift +++ b/Tests/FunctionalTests/ResourcesTests.swift @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// import Basics +import PackageModel import SPMTestSupport import XCTest @@ -126,7 +127,9 @@ class ResourcesTests: XCTestCase { } } - func testResourcesOutsideOfTargetCanBeIncluded() throws { + func testResourcesOutsideOfTargetCanBeIncluded() async throws { + try await UserToolchain.default.skipUnlessAtLeastSwift6() + try testWithTemporaryDirectory { tmpPath in let packageDir = tmpPath.appending(components: "MyPackage") diff --git a/Tests/PackageLoadingTests/ManifestLoaderCacheTests.swift b/Tests/PackageLoadingTests/ManifestLoaderCacheTests.swift index 72ad202d200..0a6e53d3b58 100644 --- a/Tests/PackageLoadingTests/ManifestLoaderCacheTests.swift +++ b/Tests/PackageLoadingTests/ManifestLoaderCacheTests.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2020-2023 Apple Inc. and the Swift project authors +// Copyright (c) 2020-2024 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information @@ -22,7 +22,10 @@ import func TSCTestSupport.withCustomEnv @available(macOS 13, iOS 16, tvOS 16, watchOS 9, *) final class ManifestLoaderCacheTests: XCTestCase { + func testDBCaching() async throws { + try await UserToolchain.default.skipUnlessAtLeastSwift6() + try await testWithTemporaryDirectory { path in let fileSystem = localFileSystem let observability = ObservabilitySystem.makeForTesting() @@ -117,6 +120,8 @@ final class ManifestLoaderCacheTests: XCTestCase { } func testInMemoryCaching() async throws { + try await UserToolchain.default.skipUnlessAtLeastSwift6() + let fileSystem = InMemoryFileSystem() let observability = ObservabilitySystem.makeForTesting() @@ -206,6 +211,8 @@ final class ManifestLoaderCacheTests: XCTestCase { } func testContentBasedCaching() async throws { + try await UserToolchain.default.skipUnlessAtLeastSwift6() + try await testWithTemporaryDirectory { path in let manifest = """ import PackageDescription @@ -265,6 +272,8 @@ final class ManifestLoaderCacheTests: XCTestCase { } func testCacheInvalidationOnEnv() async throws { + try await UserToolchain.default.skipUnlessAtLeastSwift6() + try await testWithTemporaryDirectory { path in let fileSystem = InMemoryFileSystem() let observability = ObservabilitySystem.makeForTesting() @@ -330,6 +339,8 @@ final class ManifestLoaderCacheTests: XCTestCase { } func testCacheDoNotInvalidationExpectedEnv() async throws { + try await UserToolchain.default.skipUnlessAtLeastSwift6() + try await testWithTemporaryDirectory { path in let fileSystem = InMemoryFileSystem() let observability = ObservabilitySystem.makeForTesting() @@ -415,6 +426,8 @@ final class ManifestLoaderCacheTests: XCTestCase { } func testInMemoryCacheHappyCase() async throws { + try await UserToolchain.default.skipUnlessAtLeastSwift6() + let content = """ import PackageDescription let package = Package( diff --git a/Tests/PackageLoadingTests/PD_6_0_LoadingTests.swift b/Tests/PackageLoadingTests/PD_6_0_LoadingTests.swift index 41602560bdb..8e6144278bf 100644 --- a/Tests/PackageLoadingTests/PD_6_0_LoadingTests.swift +++ b/Tests/PackageLoadingTests/PD_6_0_LoadingTests.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2023 Apple Inc. and the Swift project authors +// Copyright (c) 2023-2024 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information @@ -16,12 +16,14 @@ import SourceControl import SPMTestSupport import XCTest -class PackageDescription6_0LoadingTests: PackageDescriptionLoadingTests { +final class PackageDescription6_0LoadingTests: PackageDescriptionLoadingTests { override var toolsVersion: ToolsVersion { .v6_0 } func testPackageContextGitStatus() async throws { + try await UserToolchain.default.skipUnlessAtLeastSwift6() + let content = """ import PackageDescription let package = Package(name: "\\(Context.gitInformation?.hasUncommittedChanges == true)") @@ -34,6 +36,8 @@ class PackageDescription6_0LoadingTests: PackageDescriptionLoadingTests { } func testPackageContextGitTag() async throws { + try await UserToolchain.default.skipUnlessAtLeastSwift6() + let content = """ import PackageDescription let package = Package(name: "\\(Context.gitInformation?.currentTag ?? "")") @@ -46,6 +50,8 @@ class PackageDescription6_0LoadingTests: PackageDescriptionLoadingTests { } func testPackageContextGitCommit() async throws { + try await UserToolchain.default.skipUnlessAtLeastSwift6() + let content = """ import PackageDescription let package = Package(name: "\\(Context.gitInformation?.currentCommit ?? "")") diff --git a/Tests/PackageLoadingTests/PackageBuilderTests.swift b/Tests/PackageLoadingTests/PackageBuilderTests.swift index 4b24f5779bf..a9e80bac5b3 100644 --- a/Tests/PackageLoadingTests/PackageBuilderTests.swift +++ b/Tests/PackageLoadingTests/PackageBuilderTests.swift @@ -3023,7 +3023,7 @@ final class PackageBuilderTests: XCTestCase { } } - func testSwiftLanguageVesionPerTarget() throws { + func testSwiftLanguageVersionPerTarget() throws { let fs = InMemoryFileSystem(emptyFiles: "/Sources/foo/foo.swift", "/Sources/bar/bar.swift" diff --git a/Tests/PackageModelTests/ToolsVersionTests.swift b/Tests/PackageModelTests/ToolsVersionTests.swift index e80c36235b5..8d3ab3feaeb 100644 --- a/Tests/PackageModelTests/ToolsVersionTests.swift +++ b/Tests/PackageModelTests/ToolsVersionTests.swift @@ -91,8 +91,12 @@ class ToolsVersionTests: XCTestCase { XCTAssertEqual(ToolsVersion(string: version)?.swiftLanguageVersion.description, "4.2") } - for version in ["5.0.0", "5.1.9", "6.0.0", "7.0.0"] { + for version in ["5.0.0", "5.1.9"] { XCTAssertEqual(ToolsVersion(string: version)?.swiftLanguageVersion.description, "5") } + + for version in ["6.0.0", "7.0.0"] { + XCTAssertEqual(ToolsVersion(string: version)?.swiftLanguageVersion.description, "6") + } } } diff --git a/Tests/WorkspaceTests/InitTests.swift b/Tests/WorkspaceTests/InitTests.swift index 473254d0232..dc3fc388b6a 100644 --- a/Tests/WorkspaceTests/InitTests.swift +++ b/Tests/WorkspaceTests/InitTests.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2014-2023 Apple Inc. and the Swift project authors +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information @@ -16,7 +16,7 @@ import PackageModel import Workspace import XCTest -class InitTests: XCTestCase { +final class InitTests: XCTestCase { // MARK: TSCBasic package creation for each package type. @@ -53,8 +53,10 @@ class InitTests: XCTestCase { XCTAssertMatch(manifestContents, .contains(packageWithNameOnly(named: name))) } } - - func testInitPackageExecutable() throws { + + func testInitPackageExecutable() async throws { + try await UserToolchain.default.skipUnlessAtLeastSwift6() + try testWithTemporaryDirectory { tmpPath in let fs = localFileSystem let path = tmpPath.appending("Foo") @@ -98,7 +100,9 @@ class InitTests: XCTestCase { } } - func testInitPackageLibraryWithXCTestOnly() throws { + func testInitPackageLibraryWithXCTestOnly() async throws { + try await UserToolchain.default.skipUnlessAtLeastSwift6() + try testWithTemporaryDirectory { tmpPath in let fs = localFileSystem let path = tmpPath.appending("Foo") @@ -148,7 +152,7 @@ class InitTests: XCTestCase { XCTAssertFileExists(path.appending(components: ".build", triple.platformBuildPathComponent, "debug", "Modules", "Foo.swiftmodule")) } } - + func testInitPackageLibraryWithSwiftTestingOnly() throws { try testWithTemporaryDirectory { tmpPath in let fs = localFileSystem @@ -235,7 +239,9 @@ class InitTests: XCTestCase { } } - func testInitPackageLibraryWithNoTests() throws { + func testInitPackageLibraryWithNoTests() async throws { + try await UserToolchain.default.skipUnlessAtLeastSwift6() + try testWithTemporaryDirectory { tmpPath in let fs = localFileSystem let path = tmpPath.appending("Foo") @@ -339,8 +345,9 @@ class InitTests: XCTestCase { } // MARK: Special case testing - - func testInitPackageNonc99Directory() throws { + + func testInitPackageNonc99Directory() async throws { + try await UserToolchain.default.skipUnlessAtLeastSwift6() try withTemporaryDirectory(removeTreeOnDeinit: true) { tempDirPath in XCTAssertDirectoryExists(tempDirPath) @@ -367,7 +374,9 @@ class InitTests: XCTestCase { } } - func testNonC99NameExecutablePackage() throws { + func testNonC99NameExecutablePackage() async throws { + try await UserToolchain.default.skipUnlessAtLeastSwift6() + try withTemporaryDirectory(removeTreeOnDeinit: true) { tempDirPath in XCTAssertDirectoryExists(tempDirPath) diff --git a/Tests/WorkspaceTests/ManifestSourceGenerationTests.swift b/Tests/WorkspaceTests/ManifestSourceGenerationTests.swift index a1643f75595..1833c2bd8c2 100644 --- a/Tests/WorkspaceTests/ManifestSourceGenerationTests.swift +++ b/Tests/WorkspaceTests/ManifestSourceGenerationTests.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2020-2021 Apple Inc. and the Swift project authors +// Copyright (c) 2020-2024 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information @@ -33,8 +33,7 @@ extension String { } } -class ManifestSourceGenerationTests: XCTestCase { - +final class ManifestSourceGenerationTests: XCTestCase { /// Private function that writes the contents of a package manifest to a temporary package directory and then loads it, then serializes the loaded manifest back out again and loads it once again, after which it compares that no information was lost. Return the source of the newly generated manifest. @discardableResult private func testManifestWritingRoundTrip( @@ -591,6 +590,7 @@ class ManifestSourceGenerationTests: XCTestCase { } func testManifestGenerationWithSwiftLanguageVersion() async throws { + try await UserToolchain.default.skipUnlessAtLeastSwift6() let manifest = Manifest.createRootManifest( displayName: "pkg", path: "/pkg", diff --git a/Tests/WorkspaceTests/WorkspaceTests.swift b/Tests/WorkspaceTests/WorkspaceTests.swift index 584ff82d7bd..ef7df48a69b 100644 --- a/Tests/WorkspaceTests/WorkspaceTests.swift +++ b/Tests/WorkspaceTests/WorkspaceTests.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2014-2023 Apple Inc. and the Swift project authors +// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information @@ -155,7 +155,7 @@ final class WorkspaceTests: XCTestCase { } } - func testInterpreterFlags() throws { + func testInterpreterFlags() async throws { let fs = localFileSystem try testWithTemporaryDirectory { path in @@ -214,7 +214,7 @@ final class WorkspaceTests: XCTestCase { """ ) - XCTAssertMatch(ws.interpreterFlags(for: foo), [.equal("-swift-version"), .equal("5")]) + XCTAssertMatch(ws.interpreterFlags(for: foo), [.equal("-swift-version"), .equal("6")]) } } } @@ -5252,6 +5252,8 @@ final class WorkspaceTests: XCTestCase { // This verifies that the simplest possible loading APIs are available for package clients. func testSimpleAPI() async throws { + try await UserToolchain.default.skipUnlessAtLeastSwift6() + try await testWithTemporaryDirectory { path in // Create a temporary package as a test case. let packagePath = path.appending("MyPkg") From e53bb511ac6da8bd34756d058bf579be9f9c5057 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 9 May 2024 11:30:20 -0700 Subject: [PATCH 143/159] [PackageModel] SwiftTarget: Rename `swiftVersion` into `toolsSwiftVersion` The value of this field is computed based on the tools version of the manifest and is intended to be used as a fallback if there is no swift language version specified in the build settings for a swift target. --- .../SwiftTargetBuildDescription.swift | 2 +- Sources/Build/BuildPlan/BuildPlan+Test.swift | 2 +- Sources/PackageLoading/PackageBuilder.swift | 6 +++--- Sources/PackageModel/Target/SwiftTarget.swift | 18 +++++++++--------- .../SPMTestSupport/ResolvedTarget+Mock.swift | 2 +- Sources/XCBuildSupport/PIFBuilder.swift | 6 +++--- .../PackageBuilderTests.swift | 2 +- 7 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift index b1fa8e06039..776887730d8 100644 --- a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift @@ -148,7 +148,7 @@ package final class SwiftTargetBuildDescription { /// The swift version for this target. var swiftVersion: SwiftLanguageVersion { - self.swiftTarget.swiftVersion + self.swiftTarget.toolSwiftVersion } /// Describes the purpose of a test target, including any special roles such as containing a list of discovered diff --git a/Sources/Build/BuildPlan/BuildPlan+Test.swift b/Sources/Build/BuildPlan/BuildPlan+Test.swift index 23c03da9363..e985aab80fb 100644 --- a/Sources/Build/BuildPlan/BuildPlan+Test.swift +++ b/Sources/Build/BuildPlan/BuildPlan+Test.swift @@ -268,7 +268,7 @@ private extension PackageModel.SwiftTarget { sources: sources, dependencies: dependencies, packageAccess: packageAccess, - swiftVersion: .v5, + toolsSwiftVersion: .v5, usesUnsafeFlags: false ) } diff --git a/Sources/PackageLoading/PackageBuilder.swift b/Sources/PackageLoading/PackageBuilder.swift index 59d5b972402..4de1310a5a7 100644 --- a/Sources/PackageLoading/PackageBuilder.swift +++ b/Sources/PackageLoading/PackageBuilder.swift @@ -997,7 +997,7 @@ public final class PackageBuilder { others: others, dependencies: dependencies, packageAccess: potentialModule.packageAccess, - swiftVersion: self.swiftVersion(), + toolsSwiftVersion: self.toolsSwiftVersion(), declaredSwiftVersions: self.declaredSwiftVersions(), buildSettings: buildSettings, buildSettingsDescription: manifestTarget.settings, @@ -1237,7 +1237,7 @@ public final class PackageBuilder { } /// Computes the swift version to use for this manifest. - private func swiftVersion() throws -> SwiftLanguageVersion { + private func toolsSwiftVersion() throws -> SwiftLanguageVersion { if let swiftVersion = self.swiftVersionCache { return swiftVersion } @@ -1738,7 +1738,7 @@ extension PackageBuilder { sources: sources, dependencies: dependencies, packageAccess: false, - swiftVersion: self.swiftVersion(), + toolsSwiftVersion: self.toolsSwiftVersion(), buildSettings: buildSettings, buildSettingsDescription: targetDescription.settings, usesUnsafeFlags: false diff --git a/Sources/PackageModel/Target/SwiftTarget.swift b/Sources/PackageModel/Target/SwiftTarget.swift index c7428bbbb5b..826ad879ed7 100644 --- a/Sources/PackageModel/Target/SwiftTarget.swift +++ b/Sources/PackageModel/Target/SwiftTarget.swift @@ -23,7 +23,7 @@ public final class SwiftTarget: Target { } public init(name: String, dependencies: [Target.Dependency], packageAccess: Bool, testDiscoverySrc: Sources) { - self.swiftVersion = .v5 + self.toolSwiftVersion = .v5 self.declaredSwiftVersions = [] super.init( @@ -40,8 +40,8 @@ public final class SwiftTarget: Target { ) } - /// The swift version of this target. - public let swiftVersion: SwiftLanguageVersion + /// The swift language version that is computed for this target based on tools version of the manifest. + public let toolSwiftVersion: SwiftLanguageVersion /// The list of swift versions declared by the manifest. public let declaredSwiftVersions: [SwiftLanguageVersion] @@ -57,14 +57,14 @@ public final class SwiftTarget: Target { others: [AbsolutePath] = [], dependencies: [Target.Dependency] = [], packageAccess: Bool, - swiftVersion: SwiftLanguageVersion, + toolsSwiftVersion: SwiftLanguageVersion, declaredSwiftVersions: [SwiftLanguageVersion] = [], buildSettings: BuildSettings.AssignmentTable = .init(), buildSettingsDescription: [TargetBuildSettingDescription.Setting] = [], pluginUsages: [PluginUsage] = [], usesUnsafeFlags: Bool ) { - self.swiftVersion = swiftVersion + self.toolSwiftVersion = toolsSwiftVersion self.declaredSwiftVersions = declaredSwiftVersions super.init( name: name, @@ -103,8 +103,8 @@ public final class SwiftTarget: Target { // We need to select the latest Swift language version that can // satisfy the current tools version but there is not a good way to // do that currently. - self.swiftVersion = swiftTestTarget? - .swiftVersion ?? SwiftLanguageVersion(string: String(SwiftVersion.current.major)) ?? .v4 + self.toolSwiftVersion = swiftTestTarget? + .toolSwiftVersion ?? SwiftLanguageVersion(string: String(SwiftVersion.current.major)) ?? .v4 self.declaredSwiftVersions = [] let sources = Sources(paths: [testEntryPointPath], root: testEntryPointPath.parentDirectory) @@ -129,14 +129,14 @@ public final class SwiftTarget: Target { override public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(self.swiftVersion, forKey: .swiftVersion) + try container.encode(self.toolSwiftVersion, forKey: .swiftVersion) try container.encode(self.declaredSwiftVersions, forKey: .declaredSwiftVersions) try super.encode(to: encoder) } public required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - self.swiftVersion = try container.decode(SwiftLanguageVersion.self, forKey: .swiftVersion) + self.toolSwiftVersion = try container.decode(SwiftLanguageVersion.self, forKey: .swiftVersion) self.declaredSwiftVersions = try container.decode([SwiftLanguageVersion].self, forKey: .declaredSwiftVersions) try super.init(from: decoder) } diff --git a/Sources/SPMTestSupport/ResolvedTarget+Mock.swift b/Sources/SPMTestSupport/ResolvedTarget+Mock.swift index 2a28903dd88..57cee941fe7 100644 --- a/Sources/SPMTestSupport/ResolvedTarget+Mock.swift +++ b/Sources/SPMTestSupport/ResolvedTarget+Mock.swift @@ -29,7 +29,7 @@ extension ResolvedModule { sources: Sources(paths: [], root: "/"), dependencies: [], packageAccess: false, - swiftVersion: .v4, + toolsSwiftVersion: .v4, usesUnsafeFlags: false ), dependencies: deps.map { .target($0, conditions: conditions) }, diff --git a/Sources/XCBuildSupport/PIFBuilder.swift b/Sources/XCBuildSupport/PIFBuilder.swift index 6907cf4af38..ce19afeb19b 100644 --- a/Sources/XCBuildSupport/PIFBuilder.swift +++ b/Sources/XCBuildSupport/PIFBuilder.swift @@ -1584,19 +1584,19 @@ extension SwiftTarget { }) // If we were able to determine the list of versions supported by XCBuild, cross-reference with the package's // Swift versions in case the preferred version isn't available. - if !supportedSwiftVersions.isEmpty, !supportedSwiftVersions.contains(self.swiftVersion) { + if !supportedSwiftVersions.isEmpty, !supportedSwiftVersions.contains(self.toolSwiftVersion) { let declaredVersions = Array(normalizedDeclaredVersions.intersection(supportedSwiftVersions)).sorted(by: >) if let swiftVersion = declaredVersions.first { return swiftVersion } else { throw PIFGenerationError.unsupportedSwiftLanguageVersion( targetName: self.name, - version: self.swiftVersion, + version: self.toolSwiftVersion, supportedVersions: supportedSwiftVersions ) } } - return self.swiftVersion + return self.toolSwiftVersion } } diff --git a/Tests/PackageLoadingTests/PackageBuilderTests.swift b/Tests/PackageLoadingTests/PackageBuilderTests.swift index a9e80bac5b3..b14cd7c7743 100644 --- a/Tests/PackageLoadingTests/PackageBuilderTests.swift +++ b/Tests/PackageLoadingTests/PackageBuilderTests.swift @@ -3303,7 +3303,7 @@ final class PackageBuilderTester { guard case let swiftTarget as SwiftTarget = target else { return XCTFail("\(target) is not a swift target", file: file, line: line) } - XCTAssertEqual(SwiftLanguageVersion(string: swiftVersion)!, swiftTarget.swiftVersion, file: file, line: line) + XCTAssertEqual(SwiftLanguageVersion(string: swiftVersion)!, swiftTarget.toolSwiftVersion, file: file, line: line) } func check(pluginCapability: PluginCapability, file: StaticString = #file, line: UInt = #line) { From 3bedafabd7a59bfff722b6060c6d8a23684afd8c Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 9 May 2024 11:42:03 -0700 Subject: [PATCH 144/159] [BuildDescription] SwiftTargetBuildDescription: Rename `swiftVersion` into `toolsSwiftVersion` --- .../BuildDescription/SwiftTargetBuildDescription.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift index 776887730d8..70e3e721b62 100644 --- a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift @@ -146,8 +146,8 @@ package final class SwiftTargetBuildDescription { /// Any addition flags to be added. These flags are expected to be computed during build planning. var additionalFlags: [String] = [] - /// The swift version for this target. - var swiftVersion: SwiftLanguageVersion { + /// The swift language version that is computed for this target based on tools version of the manifest. + var toolsSwiftVersion: SwiftLanguageVersion { self.swiftTarget.toolSwiftVersion } @@ -582,7 +582,7 @@ package final class SwiftTargetBuildDescription { // Fallback to package wide setting if there is no target specific version. if args.firstIndex(of: "-swift-version") == nil { - args += ["-swift-version", self.swiftVersion.rawValue] + args += ["-swift-version", self.toolsSwiftVersion.rawValue] } // Add the output for the `.swiftinterface`, if requested or if library evolution has been enabled some other From e843f4e2d7ec56045140d1106555b68efc21d8b4 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Fri, 10 May 2024 18:57:53 -0400 Subject: [PATCH 145/159] Pass through `--experimental-event-stream-version` to swift-testing. (#7551) Follow-on to #7534. swift-testing has an additional argument specifying the schema version of the JSON being used that we also need to pass through SwiftPM. See: https://github.com/apple/swift-testing/pull/383 --- Sources/Commands/SwiftTestCommand.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Sources/Commands/SwiftTestCommand.swift b/Sources/Commands/SwiftTestCommand.swift index d0aae87ce7e..fb8de86ed71 100644 --- a/Sources/Commands/SwiftTestCommand.swift +++ b/Sources/Commands/SwiftTestCommand.swift @@ -164,6 +164,11 @@ struct TestCommandOptions: ParsableArguments { @Option(name: .customLong("experimental-event-stream-output"), help: .hidden) var eventStreamOutputPath: AbsolutePath? + + /// The schema version of swift-testing's JSON input/output. + @Option(name: .customLong("experimental-event-stream-version"), + help: .hidden) + var eventStreamVersion: Int? } /// Tests filtering specifier, which is used to filter tests to run. From 9183b7cb0c22798e4abbf4c3a2f1476785133b46 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 10 May 2024 09:55:28 -0700 Subject: [PATCH 146/159] [PackageModel] Associated swift version build setting with `SWIFT_VERSION` declaration Instead of putting it into `OTHER_SWIFT_FLAGS`, let's introduce a dedicated declaration scope - `SWIFT_VERSION` to ease handling and discovery. Resolves: rdar://127883018 --- .../BuildDescription/SwiftTargetBuildDescription.swift | 3 +++ Sources/PackageLoading/PackageBuilder.swift | 4 ++-- Sources/PackageModel/BuildSettings.swift | 1 + Tests/BuildTests/BuildPlanTests.swift | 8 ++++---- Tests/PackageLoadingTests/PackageBuilderTests.swift | 10 +++++----- 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift index 70e3e721b62..cd404e012f0 100644 --- a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift @@ -825,6 +825,9 @@ package final class SwiftTargetBuildDescription { let scope = self.defaultBuildParameters.createScope(for: self.target) var flags: [String] = [] + // A custom swift version. + flags += scope.evaluate(.SWIFT_VERSION).flatMap { ["-swift-version", $0] } + // Swift defines. let swiftDefines = scope.evaluate(.SWIFT_ACTIVE_COMPILATION_CONDITIONS) flags += swiftDefines.map { "-D" + $0 } diff --git a/Sources/PackageLoading/PackageBuilder.swift b/Sources/PackageLoading/PackageBuilder.swift index 4de1310a5a7..25dd8233853 100644 --- a/Sources/PackageLoading/PackageBuilder.swift +++ b/Sources/PackageLoading/PackageBuilder.swift @@ -1176,10 +1176,10 @@ public final class PackageBuilder { throw InternalError("only Swift supports swift language version") case .swift: - decl = .OTHER_SWIFT_FLAGS + decl = .SWIFT_VERSION } - values = ["-swift-version", version.rawValue] + values = [version.rawValue] } // Create an assignment for this setting. diff --git a/Sources/PackageModel/BuildSettings.swift b/Sources/PackageModel/BuildSettings.swift index 14951dce9be..7d9c7805747 100644 --- a/Sources/PackageModel/BuildSettings.swift +++ b/Sources/PackageModel/BuildSettings.swift @@ -18,6 +18,7 @@ public enum BuildSettings { // Swift. public static let SWIFT_ACTIVE_COMPILATION_CONDITIONS: Declaration = .init("SWIFT_ACTIVE_COMPILATION_CONDITIONS") public static let OTHER_SWIFT_FLAGS: Declaration = .init("OTHER_SWIFT_FLAGS") + public static let SWIFT_VERSION: Declaration = .init("SWIFT_VERSION") // C family. public static let GCC_PREPROCESSOR_DEFINITIONS: Declaration = .init("GCC_PREPROCESSOR_DEFINITIONS") diff --git a/Tests/BuildTests/BuildPlanTests.swift b/Tests/BuildTests/BuildPlanTests.swift index 4b86b47335a..feb850f6e32 100644 --- a/Tests/BuildTests/BuildPlanTests.swift +++ b/Tests/BuildTests/BuildPlanTests.swift @@ -4083,7 +4083,7 @@ final class BuildPlanTests: XCTestCase { ) let exe = try result.target(for: "exe").swiftTarget().compileArguments() - XCTAssertMatch(exe, [.anySequence, "-DFOO", "-swift-version", "5", "-g", "-Xcc", "-g", "-Xcc", "-fno-omit-frame-pointer", .end]) + XCTAssertMatch(exe, [.anySequence, "-swift-version", "5", "-DFOO", "-g", "-Xcc", "-g", "-Xcc", "-fno-omit-frame-pointer", .end]) let linkExe = try result.buildProduct(for: "exe").linkArguments() XCTAssertMatch(linkExe, [.anySequence, "-lsqlite3", "-llibz", "-Ilfoo", "-L", "lbar", "-g", .end]) @@ -4149,7 +4149,7 @@ final class BuildPlanTests: XCTestCase { ) let exe = try result.target(for: "exe").swiftTarget().compileArguments() - XCTAssertMatch(exe, [.anySequence, "-DFOO", "-swift-version", "5", "-g", "-Xcc", "-g", "-Xcc", "-fomit-frame-pointer", .end]) + XCTAssertMatch(exe, [.anySequence, "-swift-version", "5", "-DFOO", "-g", "-Xcc", "-g", "-Xcc", "-fomit-frame-pointer", .end]) } // omit frame pointers explicitly set to false @@ -4206,7 +4206,7 @@ final class BuildPlanTests: XCTestCase { ) let exe = try result.target(for: "exe").swiftTarget().compileArguments() - XCTAssertMatch(exe, [.anySequence, "-DFOO", "-swift-version", "5", "-g", "-Xcc", "-g", "-Xcc", "-fno-omit-frame-pointer", .end]) + XCTAssertMatch(exe, [.anySequence, "-swift-version", "5", "-DFOO", "-g", "-Xcc", "-g", "-Xcc", "-fno-omit-frame-pointer", .end]) } do { @@ -4253,10 +4253,10 @@ final class BuildPlanTests: XCTestCase { exe, [ .anySequence, + "-swift-version", "4", "-DFOO", "-cxx-interoperability-mode=default", "-Xcc", "-std=c++17", - "-swift-version", "4", "-g", "-Xcc", "-g", .end, diff --git a/Tests/PackageLoadingTests/PackageBuilderTests.swift b/Tests/PackageLoadingTests/PackageBuilderTests.swift index b14cd7c7743..289b8fb5b03 100644 --- a/Tests/PackageLoadingTests/PackageBuilderTests.swift +++ b/Tests/PackageLoadingTests/PackageBuilderTests.swift @@ -3055,13 +3055,13 @@ final class PackageBuilderTests: XCTestCase { package.target.buildSettings, environment: BuildEnvironment(platform: .macOS, configuration: .debug) ) - XCTAssertEqual(macosDebugScope.evaluate(.OTHER_SWIFT_FLAGS), ["-swift-version", "5"]) + XCTAssertEqual(macosDebugScope.evaluate(.SWIFT_VERSION), ["5"]) let macosReleaseScope = BuildSettings.Scope( package.target.buildSettings, environment: BuildEnvironment(platform: .macOS, configuration: .release) ) - XCTAssertEqual(macosReleaseScope.evaluate(.OTHER_SWIFT_FLAGS), ["-swift-version", "5"]) + XCTAssertEqual(macosReleaseScope.evaluate(.SWIFT_VERSION), ["5"]) } package.checkModule("bar") { package in @@ -3069,19 +3069,19 @@ final class PackageBuilderTests: XCTestCase { package.target.buildSettings, environment: BuildEnvironment(platform: .linux, configuration: .debug) ) - XCTAssertEqual(linuxDebugScope.evaluate(.OTHER_SWIFT_FLAGS), ["-swift-version", "3"]) + XCTAssertEqual(linuxDebugScope.evaluate(.SWIFT_VERSION), ["3"]) let macosDebugScope = BuildSettings.Scope( package.target.buildSettings, environment: BuildEnvironment(platform: .macOS, configuration: .debug) ) - XCTAssertEqual(macosDebugScope.evaluate(.OTHER_SWIFT_FLAGS), ["-swift-version", "4"]) + XCTAssertEqual(macosDebugScope.evaluate(.SWIFT_VERSION), ["4"]) let macosReleaseScope = BuildSettings.Scope( package.target.buildSettings, environment: BuildEnvironment(platform: .macOS, configuration: .release) ) - XCTAssertEqual(macosReleaseScope.evaluate(.OTHER_SWIFT_FLAGS), []) + XCTAssertEqual(macosReleaseScope.evaluate(.SWIFT_VERSION), []) } } } From daea5225e12d5b46b84e558af607666c5eb4e9c8 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Mon, 13 May 2024 20:52:39 -0700 Subject: [PATCH 147/159] Add two `Sendable` annotations to enable building SourceKit-LSP in Swift 6 mode (#7553) - Mark `FileRuleDescription` as `Sendable` - Make `Platform` sendable and make `Platform.current` a constant --- Sources/PackageLoading/Platform.swift | 8 +++----- Sources/PackageLoading/TargetSourcesBuilder.swift | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Sources/PackageLoading/Platform.swift b/Sources/PackageLoading/Platform.swift index 324f0593458..297954498c1 100644 --- a/Sources/PackageLoading/Platform.swift +++ b/Sources/PackageLoading/Platform.swift @@ -20,23 +20,21 @@ private func isAndroid() -> Bool { (try? localFileSystem.isFile(AbsolutePath(validating: "/system/bin/toybox"))) ?? false } -public enum Platform: Equatable { +public enum Platform: Equatable, Sendable { case android case darwin case linux(LinuxFlavor) case windows /// Recognized flavors of linux. - public enum LinuxFlavor: Equatable { + public enum LinuxFlavor: Equatable, Sendable { case debian case fedora } } extension Platform { - // This is not just a computed property because the ToolchainRegistryTests - // change the value. - public static var current: Platform? = { + public static let current: Platform? = { #if os(Windows) return .windows #else diff --git a/Sources/PackageLoading/TargetSourcesBuilder.swift b/Sources/PackageLoading/TargetSourcesBuilder.swift index 7e036534f59..bff05448bf4 100644 --- a/Sources/PackageLoading/TargetSourcesBuilder.swift +++ b/Sources/PackageLoading/TargetSourcesBuilder.swift @@ -563,7 +563,7 @@ public struct TargetSourcesBuilder { } /// Describes a rule for including a source or resource file in a target. -public struct FileRuleDescription { +public struct FileRuleDescription: Sendable { /// A rule semantically describes a file/directory in a target. /// /// It is up to the build system to translate a rule into a build command. From 6ed0ce37533565c9cbfc8698351decb16a4b2324 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Mon, 13 May 2024 20:53:06 -0700 Subject: [PATCH 148/159] =?UTF-8?q?Don=E2=80=99t=20disable=20Swift=206=20m?= =?UTF-8?q?ode=20in=20swift-syntax=20(#7545)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This shouldn’t be needed anymore with https://github.com/apple/swift-package-manager/pull/7504. --- Utilities/bootstrap | 2 -- Utilities/build-using-self | 2 -- 2 files changed, 4 deletions(-) diff --git a/Utilities/bootstrap b/Utilities/bootstrap index ea00fad37d3..23bb9bd8e91 100755 --- a/Utilities/bootstrap +++ b/Utilities/bootstrap @@ -747,8 +747,6 @@ def get_swiftpm_env_cmd(args): env_cmd.append("SWIFTPM_LLBUILD_FWK=1") env_cmd.append("SWIFTCI_USE_LOCAL_DEPS=1") env_cmd.append("SWIFTPM_MACOS_DEPLOYMENT_TARGET=%s" % g_macos_deployment_target) - # Disable Swift 6 mode in swift-syntax to work around rdar://126952308 - env_cmd.append("SWIFTSYNTAX_DISABLE_SWIFT_6_MODE=1") if not '-macosx' in args.build_target and args.command == 'install': env_cmd.append("SWIFTCI_INSTALL_RPATH_OS=%s" % args.platform_path.group(2)) diff --git a/Utilities/build-using-self b/Utilities/build-using-self index 57a7eed54c2..1d6a1552993 100755 --- a/Utilities/build-using-self +++ b/Utilities/build-using-self @@ -20,8 +20,6 @@ echo "Current directory is ${PWD}" CONFIGURATION=debug export SWIFTCI_IS_SELF_HOSTED=1 -# Disable Swift 6 mode in swift-syntax to work around rdar://126952308 -export SWIFTSYNTAX_DISABLE_SWIFT_6_MODE=1 set -x From 4f01a848d1dc3264d09a7f199beeac0d3a2443d2 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Tue, 14 May 2024 11:06:15 -0700 Subject: [PATCH 149/159] Expose the build triple of a `BuildTarget` in `SourceKitLSPAPI` (#7555) If we have a package like this ```swift let package = Package( name: "MyLibrary", targets: [ .target(name: "Lib"), .executableTarget(name: "MyExec", dependencies: ["Lib"]), .plugin( name: "MyPlugin", capability: .command( intent: .sourceCodeFormatting(), permissions: [] ), dependencies: ["MyExec"] ) ] ) ``` Then the package graph contains two targets for `Lib`. We need to be able to differentiate them in SourceKit-LSP. For that, we need to expose the build triple of a `BuildTarget`. --- Sources/SourceKitLSPAPI/BuildDescription.swift | 13 +++++++++++++ .../PluginTargetBuildDescription.swift | 5 +++++ 2 files changed, 18 insertions(+) diff --git a/Sources/SourceKitLSPAPI/BuildDescription.swift b/Sources/SourceKitLSPAPI/BuildDescription.swift index ddb2030c912..74614635454 100644 --- a/Sources/SourceKitLSPAPI/BuildDescription.swift +++ b/Sources/SourceKitLSPAPI/BuildDescription.swift @@ -23,6 +23,9 @@ import class Build.ClangTargetBuildDescription import class Build.SwiftTargetBuildDescription import struct PackageGraph.ResolvedModule import struct PackageGraph.ModulesGraph +import enum PackageGraph.BuildTriple + +public typealias BuildTriple = PackageGraph.BuildTriple public protocol BuildTarget { var sources: [URL] { get } @@ -30,6 +33,8 @@ public protocol BuildTarget { /// The name of the target. It should be possible to build a target by passing this name to `swift build --target` var name: String { get } + var buildTriple: BuildTriple { get } + /// Whether the target is part of the root package that the user opened or if it's part of a package dependency. var isPartOfRootPackage: Bool { get } @@ -53,6 +58,10 @@ private struct WrappedClangTargetBuildDescription: BuildTarget { return description.clangTarget.name } + public var buildTriple: BuildTriple { + return description.target.buildTriple + } + public func compileArguments(for fileURL: URL) throws -> [String] { let filePath = try resolveSymlinks(try AbsolutePath(validating: fileURL.path)) let commandLine = try description.emitCommandLine(for: filePath) @@ -74,6 +83,10 @@ private struct WrappedSwiftTargetBuildDescription: BuildTarget { return description.target.name } + public var buildTriple: BuildTriple { + return description.target.buildTriple + } + var sources: [URL] { return description.sources.map { URL(fileURLWithPath: $0.pathString) } } diff --git a/Sources/SourceKitLSPAPI/PluginTargetBuildDescription.swift b/Sources/SourceKitLSPAPI/PluginTargetBuildDescription.swift index 5c6ab83197b..93a01d0dad3 100644 --- a/Sources/SourceKitLSPAPI/PluginTargetBuildDescription.swift +++ b/Sources/SourceKitLSPAPI/PluginTargetBuildDescription.swift @@ -17,6 +17,7 @@ import struct PackageGraph.ResolvedModule private import class PackageLoading.ManifestLoader internal import struct PackageModel.ToolsVersion private import class PackageModel.UserToolchain +import enum PackageGraph.BuildTriple struct PluginTargetBuildDescription: BuildTarget { private let target: ResolvedModule @@ -38,6 +39,10 @@ struct PluginTargetBuildDescription: BuildTarget { return target.name } + var buildTriple: BuildTriple { + return target.buildTriple + } + func compileArguments(for fileURL: URL) throws -> [String] { // FIXME: This is very odd and we should clean this up by merging `ManifestLoader` and `DefaultPluginScriptRunner` again. let loader = ManifestLoader(toolchain: try UserToolchain(swiftSDK: .hostSwiftSDK())) From ecf3e8c990fecede1e1a2fef39deec88d80b232e Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 14 May 2024 12:10:43 -0700 Subject: [PATCH 150/159] Introduce manifest editing API for "add target dependency" (#7552) As a convenience for when we want to be able to add new target dependencies, introduce a manifest editing API that adds a new target dependency to an existing target in the manifest. --- .../AddTargetDependency.swift | 103 ++++++++++++++++++ Sources/PackageModelSyntax/CMakeLists.txt | 1 + .../ManifestEditError.swift | 6 + .../ManifestEditTests.swift | 40 +++++++ 4 files changed, 150 insertions(+) create mode 100644 Sources/PackageModelSyntax/AddTargetDependency.swift diff --git a/Sources/PackageModelSyntax/AddTargetDependency.swift b/Sources/PackageModelSyntax/AddTargetDependency.swift new file mode 100644 index 00000000000..fde0a5e69e6 --- /dev/null +++ b/Sources/PackageModelSyntax/AddTargetDependency.swift @@ -0,0 +1,103 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Basics +import PackageLoading +import PackageModel +import SwiftParser +import SwiftSyntax +import SwiftSyntaxBuilder + +/// Add a target dependency to a manifest's source code. +public struct AddTargetDependency { + /// The set of argument labels that can occur after the "dependencies" + /// argument in the various target initializers. + /// + /// TODO: Could we generate this from the the PackageDescription module, so + /// we don't have keep it up-to-date manually? + private static let argumentLabelsAfterDependencies: Set = [ + "path", + "exclude", + "sources", + "resources", + "publicHeadersPath", + "packageAccess", + "cSettings", + "cxxSettings", + "swiftSettings", + "linkerSettings", + "plugins", + ] + + /// Produce the set of source edits needed to add the given target + /// dependency to the given manifest file. + public static func addTargetDependency( + _ dependency: TargetDescription.Dependency, + targetName: String, + to manifest: SourceFileSyntax + ) throws -> PackageEditResult { + // Make sure we have a suitable tools version in the manifest. + try manifest.checkEditManifestToolsVersion() + + guard let packageCall = manifest.findCall(calleeName: "Package") else { + throw ManifestEditError.cannotFindPackage + } + + // Dig out the array of targets. + guard let targetsArgument = packageCall.findArgument(labeled: "targets"), + let targetArray = targetsArgument.expression.findArrayArgument() else { + throw ManifestEditError.cannotFindTargets + } + + // Look for a call whose name is a string literal matching the + // requested target name. + func matchesTargetCall(call: FunctionCallExprSyntax) -> Bool { + guard let nameArgument = call.findArgument(labeled: "name") else { + return false + } + + guard let stringLiteral = nameArgument.expression.as(StringLiteralExprSyntax.self), + let literalValue = stringLiteral.representedLiteralValue else { + return false + } + + return literalValue == targetName + } + + guard let targetCall = FunctionCallExprSyntax.findFirst(in: targetArray, matching: matchesTargetCall) else { + throw ManifestEditError.cannotFindTarget(targetName: targetName) + } + + let newTargetCall = try addTargetDependencyLocal( + dependency, to: targetCall + ) + + return PackageEditResult( + manifestEdits: [ + .replace(targetCall, with: newTargetCall.description) + ] + ) + } + + /// Implementation of adding a target dependency to an existing call. + static func addTargetDependencyLocal( + _ dependency: TargetDescription.Dependency, + to targetCall: FunctionCallExprSyntax + ) throws -> FunctionCallExprSyntax { + try targetCall.appendingToArrayArgument( + label: "dependencies", + trailingLabels: Self.argumentLabelsAfterDependencies, + newElement: dependency.asSyntax() + ) + } +} + diff --git a/Sources/PackageModelSyntax/CMakeLists.txt b/Sources/PackageModelSyntax/CMakeLists.txt index cfab869efc1..c034d8d1705 100644 --- a/Sources/PackageModelSyntax/CMakeLists.txt +++ b/Sources/PackageModelSyntax/CMakeLists.txt @@ -10,6 +10,7 @@ add_library(PackageModelSyntax AddPackageDependency.swift AddProduct.swift AddTarget.swift + AddTargetDependency.swift ManifestEditError.swift ManifestSyntaxRepresentable.swift PackageDependency+Syntax.swift diff --git a/Sources/PackageModelSyntax/ManifestEditError.swift b/Sources/PackageModelSyntax/ManifestEditError.swift index cba8eb520dd..aaaf3351166 100644 --- a/Sources/PackageModelSyntax/ManifestEditError.swift +++ b/Sources/PackageModelSyntax/ManifestEditError.swift @@ -18,6 +18,8 @@ import SwiftSyntax /// package manifest programattically. package enum ManifestEditError: Error { case cannotFindPackage + case cannotFindTargets + case cannotFindTarget(targetName: String) case cannotFindArrayLiteralArgument(argumentName: String, node: Syntax) case oldManifest(ToolsVersion) } @@ -33,6 +35,10 @@ extension ManifestEditError: CustomStringConvertible { switch self { case .cannotFindPackage: "invalid manifest: unable to find 'Package' declaration" + case .cannotFindTargets: + "unable to find package targets in manifest" + case .cannotFindTarget(targetName: let name): + "unable to find target named '\(name)' in package" case .cannotFindArrayLiteralArgument(argumentName: let name, node: _): "unable to find array literal for '\(name)' argument" case .oldManifest(let version): diff --git a/Tests/PackageModelSyntaxTests/ManifestEditTests.swift b/Tests/PackageModelSyntaxTests/ManifestEditTests.swift index 812b72bfe84..614ca912d3e 100644 --- a/Tests/PackageModelSyntaxTests/ManifestEditTests.swift +++ b/Tests/PackageModelSyntaxTests/ManifestEditTests.swift @@ -611,6 +611,46 @@ class ManifestEditTests: XCTestCase { ) } } + + func testAddTargetDependency() throws { + try assertManifestRefactor(""" + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: [ + .package(url: "https://github.com/apple/swift-testing.git", from: "0.8.0"), + ], + targets: [ + .testTarget( + name: "MyTest" + ), + ] + ) + """, + expectedManifest: """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: [ + .package(url: "https://github.com/apple/swift-testing.git", from: "0.8.0"), + ], + targets: [ + .testTarget( + name: "MyTest", + dependencies: [ + .product(name: "Testing", package: "swift-testing"), + ] + ), + ] + ) + """) { manifest in + try AddTargetDependency.addTargetDependency( + .product(name: "Testing", package: "swift-testing"), + targetName: "MyTest", + to: manifest + ) + } + } } From 30ea7389df06cd7d2cf417181de3bafb67e17ee2 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 14 May 2024 11:02:06 -0700 Subject: [PATCH 151/159] [CODEOWNERS] NFC: Remove Tom Doron --- CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index b3e12d62317..101a56c327c 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -26,4 +26,4 @@ Sources/XCBuildSupport/* @jakepetroules -* @bnbarham @MaxDesiatov @tomerd +* @bnbarham @MaxDesiatov From a2e0e713c8717b27af1ac41f31ed0bc5fcd8c985 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 14 May 2024 11:04:00 -0700 Subject: [PATCH 152/159] [CODEOWNERS] NFC: Add Jake Petroules and Francesco Mikulis-Borsoi as top-level code owners --- CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index 101a56c327c..d08f417f13b 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -26,4 +26,4 @@ Sources/XCBuildSupport/* @jakepetroules -* @bnbarham @MaxDesiatov +* @bnbarham @MaxDesiatov @jakepetroules @francescomikulis From eb3abcd56b6ef2da6be9440063f32747d54b6ecb Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 13 May 2024 14:54:07 -0700 Subject: [PATCH 153/159] [PackageModel] Add a way to specify a default setting assignment This setting is going to be used if there are no non-default assignments that match build environment conditions. --- Sources/PackageModel/BuildSettings.swift | 33 ++++++++++++++++-------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/Sources/PackageModel/BuildSettings.swift b/Sources/PackageModel/BuildSettings.swift index 7d9c7805747..269997c7179 100644 --- a/Sources/PackageModel/BuildSettings.swift +++ b/Sources/PackageModel/BuildSettings.swift @@ -12,11 +12,11 @@ /// Namespace for build settings. public enum BuildSettings { - /// Build settings declarations. public struct Declaration: Hashable, Codable { // Swift. - public static let SWIFT_ACTIVE_COMPILATION_CONDITIONS: Declaration = .init("SWIFT_ACTIVE_COMPILATION_CONDITIONS") + public static let SWIFT_ACTIVE_COMPILATION_CONDITIONS: Declaration = + .init("SWIFT_ACTIVE_COMPILATION_CONDITIONS") public static let OTHER_SWIFT_FLAGS: Declaration = .init("OTHER_SWIFT_FLAGS") public static let SWIFT_VERSION: Declaration = .init("SWIFT_VERSION") @@ -48,18 +48,23 @@ public enum BuildSettings { /// The condition associated with this assignment. public var conditions: [PackageCondition] { get { - return _conditions.map { $0.underlying } + self._conditions.map(\.underlying) } set { - _conditions = newValue.map { PackageConditionWrapper($0) } + self._conditions = newValue.map { PackageConditionWrapper($0) } } } private var _conditions: [PackageConditionWrapper] - public init() { + /// Indicates whether this assignment represents a default + /// that should be used only if no other assignments match. + public let `default`: Bool + + public init(default: Bool = false) { self._conditions = [] self.values = [] + self.default = `default` } } @@ -68,13 +73,13 @@ public enum BuildSettings { public private(set) var assignments: [Declaration: [Assignment]] public init() { - assignments = [:] + self.assignments = [:] } /// Add the given assignment to the table. - mutating public func add(_ assignment: Assignment, for decl: Declaration) { + public mutating func add(_ assignment: Assignment, for decl: Declaration) { // FIXME: We should check for duplicate assignments. - assignments[decl, default: []].append(assignment) + self.assignments[decl, default: []].append(assignment) } } @@ -101,12 +106,18 @@ public enum BuildSettings { } // Add values from each assignment if it satisfies the build environment. - let values = assignments + let allViableAssignments = assignments .lazy .filter { $0.conditions.allSatisfy { $0.satisfies(self.environment) } } - .flatMap { $0.values } - return Array(values) + let nonDefaultAssignments = allViableAssignments.filter { !$0.default } + + // If there are no non-default assignments, let's fallback to defaults. + if nonDefaultAssignments.isEmpty { + return allViableAssignments.filter(\.default).flatMap(\.values) + } + + return nonDefaultAssignments.flatMap(\.values) } } } From b0e724b7f3913a737a306f15a62d067f9a792f15 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 13 May 2024 14:56:58 -0700 Subject: [PATCH 154/159] [Package/Build] Remove `toolsSwiftVersion` from SwiftTarget in favor of a build setting Start using "default" assignments to represent tools version based swift language version setting that is going to be produced by `BuildSettings.Scope` if no other assignments match build environment conditions. This removes ambiguity from `-swift-version` selection for downstream clients. --- .../SwiftTargetBuildDescription.swift | 10 ------- Sources/Build/BuildPlan/BuildPlan+Test.swift | 1 - Sources/PackageLoading/PackageBuilder.swift | 19 ++++++++----- Sources/PackageModel/Target/SwiftTarget.swift | 27 ++++++++++--------- .../SPMTestSupport/ResolvedTarget+Mock.swift | 1 - Tests/BuildTests/BuildPlanTests.swift | 8 +++--- .../PackageBuilderTests.swift | 10 +++++-- 7 files changed, 39 insertions(+), 37 deletions(-) diff --git a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift index cd404e012f0..62b9a2c2c51 100644 --- a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift @@ -146,11 +146,6 @@ package final class SwiftTargetBuildDescription { /// Any addition flags to be added. These flags are expected to be computed during build planning. var additionalFlags: [String] = [] - /// The swift language version that is computed for this target based on tools version of the manifest. - var toolsSwiftVersion: SwiftLanguageVersion { - self.swiftTarget.toolSwiftVersion - } - /// Describes the purpose of a test target, including any special roles such as containing a list of discovered /// tests or serving as the manifest target which contains the main entry point. package enum TestTargetRole { @@ -580,11 +575,6 @@ package final class SwiftTargetBuildDescription { // Add arguments from declared build settings. args += try self.buildSettingsFlags() - // Fallback to package wide setting if there is no target specific version. - if args.firstIndex(of: "-swift-version") == nil { - args += ["-swift-version", self.toolsSwiftVersion.rawValue] - } - // Add the output for the `.swiftinterface`, if requested or if library evolution has been enabled some other // way. if self.defaultBuildParameters.driverParameters.enableParseableModuleInterfaces || args.contains("-enable-library-evolution") { diff --git a/Sources/Build/BuildPlan/BuildPlan+Test.swift b/Sources/Build/BuildPlan/BuildPlan+Test.swift index e985aab80fb..b82828608f1 100644 --- a/Sources/Build/BuildPlan/BuildPlan+Test.swift +++ b/Sources/Build/BuildPlan/BuildPlan+Test.swift @@ -268,7 +268,6 @@ private extension PackageModel.SwiftTarget { sources: sources, dependencies: dependencies, packageAccess: packageAccess, - toolsSwiftVersion: .v5, usesUnsafeFlags: false ) } diff --git a/Sources/PackageLoading/PackageBuilder.swift b/Sources/PackageLoading/PackageBuilder.swift index 25dd8233853..3a318518e1e 100644 --- a/Sources/PackageLoading/PackageBuilder.swift +++ b/Sources/PackageLoading/PackageBuilder.swift @@ -906,7 +906,8 @@ public final class PackageBuilder { let buildSettings = try self.buildSettings( for: manifestTarget, targetRoot: potentialModule.path, - cxxLanguageStandard: self.manifest.cxxLanguageStandard + cxxLanguageStandard: self.manifest.cxxLanguageStandard, + toolsSwiftVersion: self.toolsSwiftVersion() ) // Compute the path to public headers directory. @@ -997,7 +998,6 @@ public final class PackageBuilder { others: others, dependencies: dependencies, packageAccess: potentialModule.packageAccess, - toolsSwiftVersion: self.toolsSwiftVersion(), declaredSwiftVersions: self.declaredSwiftVersions(), buildSettings: buildSettings, buildSettingsDescription: manifestTarget.settings, @@ -1055,11 +1055,18 @@ public final class PackageBuilder { func buildSettings( for target: TargetDescription?, targetRoot: AbsolutePath, - cxxLanguageStandard: String? = nil + cxxLanguageStandard: String? = nil, + toolsSwiftVersion: SwiftLanguageVersion ) throws -> BuildSettings.AssignmentTable { var table = BuildSettings.AssignmentTable() guard let target else { return table } + // First let's add a default assignments for tools swift version. + var versionAssignment = BuildSettings.Assignment(default: true) + versionAssignment.values = [toolsSwiftVersion.rawValue] + + table.add(versionAssignment, for: .SWIFT_VERSION) + // Process each setting. for setting in target.settings { let decl: BuildSettings.Declaration @@ -1728,17 +1735,17 @@ extension PackageBuilder { ) buildSettings = try self.buildSettings( for: targetDescription, - targetRoot: sourceFile.parentDirectory + targetRoot: sourceFile.parentDirectory, + toolsSwiftVersion: self.toolsSwiftVersion() ) - return try SwiftTarget( + return SwiftTarget( name: name, type: .snippet, path: .root, sources: sources, dependencies: dependencies, packageAccess: false, - toolsSwiftVersion: self.toolsSwiftVersion(), buildSettings: buildSettings, buildSettingsDescription: targetDescription.settings, usesUnsafeFlags: false diff --git a/Sources/PackageModel/Target/SwiftTarget.swift b/Sources/PackageModel/Target/SwiftTarget.swift index 826ad879ed7..566c90a52c7 100644 --- a/Sources/PackageModel/Target/SwiftTarget.swift +++ b/Sources/PackageModel/Target/SwiftTarget.swift @@ -23,7 +23,6 @@ public final class SwiftTarget: Target { } public init(name: String, dependencies: [Target.Dependency], packageAccess: Bool, testDiscoverySrc: Sources) { - self.toolSwiftVersion = .v5 self.declaredSwiftVersions = [] super.init( @@ -40,9 +39,6 @@ public final class SwiftTarget: Target { ) } - /// The swift language version that is computed for this target based on tools version of the manifest. - public let toolSwiftVersion: SwiftLanguageVersion - /// The list of swift versions declared by the manifest. public let declaredSwiftVersions: [SwiftLanguageVersion] @@ -57,14 +53,12 @@ public final class SwiftTarget: Target { others: [AbsolutePath] = [], dependencies: [Target.Dependency] = [], packageAccess: Bool, - toolsSwiftVersion: SwiftLanguageVersion, declaredSwiftVersions: [SwiftLanguageVersion] = [], buildSettings: BuildSettings.AssignmentTable = .init(), buildSettingsDescription: [TargetBuildSettingDescription.Setting] = [], pluginUsages: [PluginUsage] = [], usesUnsafeFlags: Bool ) { - self.toolSwiftVersion = toolsSwiftVersion self.declaredSwiftVersions = declaredSwiftVersions super.init( name: name, @@ -99,12 +93,22 @@ public final class SwiftTarget: Target { return target.type == .test }.flatMap { $0.target as? SwiftTarget } - // FIXME: This is not very correct but doesn't matter much in practice. // We need to select the latest Swift language version that can // satisfy the current tools version but there is not a good way to // do that currently. - self.toolSwiftVersion = swiftTestTarget? - .toolSwiftVersion ?? SwiftLanguageVersion(string: String(SwiftVersion.current.major)) ?? .v4 + var buildSettings: BuildSettings.AssignmentTable = .init() + do { + let toolsSwiftVersion = swiftTestTarget?.buildSettings.assignments[.SWIFT_VERSION]? + .filter(\.default) + .filter(\.conditions.isEmpty) + .flatMap(\.values) + + var versionAssignment = BuildSettings.Assignment() + versionAssignment.values = toolsSwiftVersion ?? [String(SwiftVersion.current.major)] + + buildSettings.add(versionAssignment, for: .SWIFT_VERSION) + } + self.declaredSwiftVersions = [] let sources = Sources(paths: [testEntryPointPath], root: testEntryPointPath.parentDirectory) @@ -115,7 +119,7 @@ public final class SwiftTarget: Target { sources: sources, dependencies: dependencies, packageAccess: packageAccess, - buildSettings: .init(), + buildSettings: buildSettings, buildSettingsDescription: [], pluginUsages: [], usesUnsafeFlags: false @@ -123,20 +127,17 @@ public final class SwiftTarget: Target { } private enum CodingKeys: String, CodingKey { - case swiftVersion case declaredSwiftVersions } override public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(self.toolSwiftVersion, forKey: .swiftVersion) try container.encode(self.declaredSwiftVersions, forKey: .declaredSwiftVersions) try super.encode(to: encoder) } public required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - self.toolSwiftVersion = try container.decode(SwiftLanguageVersion.self, forKey: .swiftVersion) self.declaredSwiftVersions = try container.decode([SwiftLanguageVersion].self, forKey: .declaredSwiftVersions) try super.init(from: decoder) } diff --git a/Sources/SPMTestSupport/ResolvedTarget+Mock.swift b/Sources/SPMTestSupport/ResolvedTarget+Mock.swift index 57cee941fe7..61ddb1cf7b8 100644 --- a/Sources/SPMTestSupport/ResolvedTarget+Mock.swift +++ b/Sources/SPMTestSupport/ResolvedTarget+Mock.swift @@ -29,7 +29,6 @@ extension ResolvedModule { sources: Sources(paths: [], root: "/"), dependencies: [], packageAccess: false, - toolsSwiftVersion: .v4, usesUnsafeFlags: false ), dependencies: deps.map { .target($0, conditions: conditions) }, diff --git a/Tests/BuildTests/BuildPlanTests.swift b/Tests/BuildTests/BuildPlanTests.swift index feb850f6e32..54bee92b619 100644 --- a/Tests/BuildTests/BuildPlanTests.swift +++ b/Tests/BuildTests/BuildPlanTests.swift @@ -4068,13 +4068,13 @@ final class BuildPlanTests: XCTestCase { bar, [ .anySequence, + "-swift-version", "5", "-DLINUX", "-Isfoo", "-L", "sbar", "-cxx-interoperability-mode=default", "-Xcc", "-std=c++17", "-enable-upcoming-feature", "BestFeature", - "-swift-version", "5", "-g", "-Xcc", "-g", "-Xcc", "-fno-omit-frame-pointer", @@ -4133,6 +4133,7 @@ final class BuildPlanTests: XCTestCase { bar, [ .anySequence, + "-swift-version", "5", "-DLINUX", "-Isfoo", "-L", "sbar", @@ -4140,7 +4141,6 @@ final class BuildPlanTests: XCTestCase { "-Xcc", "-std=c++17", "-enable-upcoming-feature", "BestFeature", - "-swift-version", "5", "-g", "-Xcc", "-g", "-Xcc", "-fomit-frame-pointer", @@ -4190,6 +4190,7 @@ final class BuildPlanTests: XCTestCase { bar, [ .anySequence, + "-swift-version", "5", "-DLINUX", "-Isfoo", "-L", "sbar", @@ -4197,7 +4198,6 @@ final class BuildPlanTests: XCTestCase { "-Xcc", "-std=c++17", "-enable-upcoming-feature", "BestFeature", - "-swift-version", "5", "-g", "-Xcc", "-g", "-Xcc", "-fno-omit-frame-pointer", @@ -4234,6 +4234,7 @@ final class BuildPlanTests: XCTestCase { bar, [ .anySequence, + "-swift-version", "5", "-DDMACOS", "-Isfoo", "-L", "sbar", @@ -4241,7 +4242,6 @@ final class BuildPlanTests: XCTestCase { "-Xcc", "-std=c++17", "-enable-upcoming-feature", "BestFeature", "-enable-upcoming-feature", "WorstFeature", - "-swift-version", "5", "-g", "-Xcc", "-g", .end, diff --git a/Tests/PackageLoadingTests/PackageBuilderTests.swift b/Tests/PackageLoadingTests/PackageBuilderTests.swift index 289b8fb5b03..d42e401f7c7 100644 --- a/Tests/PackageLoadingTests/PackageBuilderTests.swift +++ b/Tests/PackageLoadingTests/PackageBuilderTests.swift @@ -3012,8 +3012,12 @@ final class PackageBuilderTests: XCTestCase { assignment.values = ["YOLO"] assignment.conditions = [PackageCondition(platforms: [.custom(name: "bestOS", oldestSupportedVersion: .unknown)])] + var versionAssignment = BuildSettings.Assignment(default: true) + versionAssignment.values = ["4"] + var settings = BuildSettings.AssignmentTable() settings.add(assignment, for: .SWIFT_ACTIVE_COMPILATION_CONDITIONS) + settings.add(versionAssignment, for: .SWIFT_VERSION) PackageBuilderTester(manifest, in: fs) { package, _ in package.checkModule("Foo") { module in @@ -3081,7 +3085,7 @@ final class PackageBuilderTests: XCTestCase { package.target.buildSettings, environment: BuildEnvironment(platform: .macOS, configuration: .release) ) - XCTAssertEqual(macosReleaseScope.evaluate(.SWIFT_VERSION), []) + XCTAssertEqual(macosReleaseScope.evaluate(.SWIFT_VERSION), ["5"]) } } } @@ -3303,7 +3307,9 @@ final class PackageBuilderTester { guard case let swiftTarget as SwiftTarget = target else { return XCTFail("\(target) is not a swift target", file: file, line: line) } - XCTAssertEqual(SwiftLanguageVersion(string: swiftVersion)!, swiftTarget.toolSwiftVersion, file: file, line: line) + let versionAssignments = swiftTarget.buildSettings.assignments[.SWIFT_VERSION]? + .filter { $0.conditions.isEmpty }.flatMap(\.values) + XCTAssertNotNil(versionAssignments?.contains(swiftVersion), file: file, line: line) } func check(pluginCapability: PluginCapability, file: StaticString = #file, line: UInt = #line) { From 3793551113210b225543cf96625638e2adde468c Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 13 May 2024 16:19:30 -0700 Subject: [PATCH 155/159] [PIF] Re-implement swift language version handling Since `toolSwiftVersion` is gone and swift language version is now handled by the build settings we need to adjust `PIFBuilder` to handle that properly. PIF build settings can handle per-target platform specific settings which are now expressible in SwiftPM via `.swiftLanguageVersion` build setting, handling of such settings is implemented in this commit. --- Sources/XCBuildSupport/PIFBuilder.swift | 152 +++++++++++++----- .../XCBuildSupportTests/PIFBuilderTests.swift | 95 ++++++++++- 2 files changed, 208 insertions(+), 39 deletions(-) diff --git a/Sources/XCBuildSupport/PIFBuilder.swift b/Sources/XCBuildSupport/PIFBuilder.swift index ce19afeb19b..be4e78b6902 100644 --- a/Sources/XCBuildSupport/PIFBuilder.swift +++ b/Sources/XCBuildSupport/PIFBuilder.swift @@ -357,11 +357,20 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { addBuildConfiguration(name: "Release", settings: releaseSettings) for product in package.products.sorted(by: { $0.name < $1.name }) { - try self.addTarget(for: product) + let productScope = observabilityScope.makeChildScope( + description: "Adding \(product.name) product", + metadata: package.underlying.diagnosticsMetadata + ) + + productScope.trap { try self.addTarget(for: product) } } for target in package.targets.sorted(by: { $0.name < $1.name }) { - try self.addTarget(for: target) + let targetScope = observabilityScope.makeChildScope( + description: "Adding \(target.name) module", + metadata: package.underlying.diagnosticsMetadata + ) + targetScope.trap { try self.addTarget(for: target) } } if self.binaryGroup.children.isEmpty { @@ -485,10 +494,7 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { settings[.GCC_C_LANGUAGE_STANDARD] = clangTarget.cLanguageStandard settings[.CLANG_CXX_LANGUAGE_STANDARD] = clangTarget.cxxLanguageStandard } else if let swiftTarget = mainTarget.underlying as? SwiftTarget { - settings[.SWIFT_VERSION] = try swiftTarget - .computeEffectiveSwiftVersion(supportedSwiftVersions: self.parameters.supportedSwiftVersions) - .description - + try settings.addSwiftVersionSettings(target: swiftTarget, parameters: self.parameters) settings.addCommonSwiftSettings(package: self.package, target: mainTarget, parameters: self.parameters) } @@ -676,9 +682,8 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder { shouldImpartModuleMap = false } } else if let swiftTarget = target.underlying as? SwiftTarget { - settings[.SWIFT_VERSION] = try swiftTarget - .computeEffectiveSwiftVersion(supportedSwiftVersions: self.parameters.supportedSwiftVersions) - .description + try settings.addSwiftVersionSettings(target: swiftTarget, parameters: self.parameters) + // Generate ObjC compatibility header for Swift library targets. settings[.SWIFT_OBJC_INTERFACE_HEADER_DIR] = "$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)" settings[.SWIFT_OBJC_INTERFACE_HEADER_NAME] = "\(target.name)-Swift.h" @@ -1575,31 +1580,6 @@ extension Target { } } -extension SwiftTarget { - func computeEffectiveSwiftVersion(supportedSwiftVersions: [SwiftLanguageVersion]) throws -> SwiftLanguageVersion { - // We have to normalize to two component strings to match the results from XCBuild w.r.t. to hashing of - // `SwiftLanguageVersion` instances. - let normalizedDeclaredVersions = Set(self.declaredSwiftVersions.compactMap { - SwiftLanguageVersion(string: "\($0.major).\($0.minor)") - }) - // If we were able to determine the list of versions supported by XCBuild, cross-reference with the package's - // Swift versions in case the preferred version isn't available. - if !supportedSwiftVersions.isEmpty, !supportedSwiftVersions.contains(self.toolSwiftVersion) { - let declaredVersions = Array(normalizedDeclaredVersions.intersection(supportedSwiftVersions)).sorted(by: >) - if let swiftVersion = declaredVersions.first { - return swiftVersion - } else { - throw PIFGenerationError.unsupportedSwiftLanguageVersion( - targetName: self.name, - version: self.toolSwiftVersion, - supportedVersions: supportedSwiftVersions - ) - } - } - return self.toolSwiftVersion - } -} - extension ProductType { var targetType: Target.Kind { switch self { @@ -1828,6 +1808,93 @@ extension PIF.PlatformFilter { } extension PIF.BuildSettings { + fileprivate mutating func addSwiftVersionSettings( + target: SwiftTarget, + parameters: PIFBuilderParameters + ) throws { + guard let versionAssignments = target.buildSettings.assignments[.SWIFT_VERSION] else { + // This should never happens in practice because there is always a default tools version based value. + return + } + + func isSupportedVersion(_ version: SwiftLanguageVersion) -> Bool { + parameters.supportedSwiftVersions.isEmpty || parameters.supportedSwiftVersions.contains(version) + } + + func computeEffectiveSwiftVersions(for versions: [SwiftLanguageVersion]) -> [String] { + versions + .filter { target.declaredSwiftVersions.contains($0) } + .filter { isSupportedVersion($0) }.map(\.description) + } + + func computeEffectiveTargetVersion(for assignment: BuildSettings.Assignment) throws -> String { + let versions = assignment.values.compactMap { SwiftLanguageVersion(string: $0) } + if let effectiveVersion = computeEffectiveSwiftVersions(for: versions).last { + return effectiveVersion + } + + throw PIFGenerationError.unsupportedSwiftLanguageVersions( + targetName: target.name, + versions: versions, + supportedVersions: parameters.supportedSwiftVersions + ) + } + + var toolsSwiftVersion: SwiftLanguageVersion? = nil + // First, check whether there are any target specific settings. + for assignment in versionAssignments { + if assignment.default { + toolsSwiftVersion = assignment.values.first.flatMap { .init(string: $0) } + continue + } + + if assignment.conditions.isEmpty { + self[.SWIFT_VERSION] = try computeEffectiveTargetVersion(for: assignment) + continue + } + + for condition in assignment.conditions { + if let platforms = condition.platformsCondition { + for platform: Platform in platforms.platforms.compactMap({ .init(rawValue: $0.name) }) { + self[.SWIFT_VERSION, for: platform] = try computeEffectiveTargetVersion(for: assignment) + } + } + } + } + + // If there were no target specific assignments, let's add a fallback tools version based value. + if let toolsSwiftVersion, self[.SWIFT_VERSION] == nil { + // Use tools based version if it's supported. + if isSupportedVersion(toolsSwiftVersion) { + self[.SWIFT_VERSION] = toolsSwiftVersion.description + return + } + + // Otherwise pick the newest supported tools version based value. + + // We have to normalize to two component strings to match the results from XCBuild w.r.t. to hashing of + // `SwiftLanguageVersion` instances. + let normalizedDeclaredVersions = Set(target.declaredSwiftVersions.compactMap { + SwiftLanguageVersion(string: "\($0.major).\($0.minor)") + }) + + let declaredSwiftVersions = Array( + normalizedDeclaredVersions + .intersection(parameters.supportedSwiftVersions) + ).sorted(by: >) + if let swiftVersion = declaredSwiftVersions.first { + self[.SWIFT_VERSION] = swiftVersion.description + return + } + + throw PIFGenerationError.unsupportedSwiftLanguageVersions( + targetName: target.name, + versions: Array(normalizedDeclaredVersions), + supportedVersions: parameters.supportedSwiftVersions + ) + } + } + fileprivate mutating func addCommonSwiftSettings( package: ResolvedPackage, target: ResolvedModule, @@ -1859,9 +1926,22 @@ extension PIF.BuildSettings.Platform { } public enum PIFGenerationError: Error { - case unsupportedSwiftLanguageVersion( + case unsupportedSwiftLanguageVersions( targetName: String, - version: SwiftLanguageVersion, + versions: [SwiftLanguageVersion], supportedVersions: [SwiftLanguageVersion] ) } + +extension PIFGenerationError: CustomStringConvertible { + public var description: String { + switch self { + case .unsupportedSwiftLanguageVersions( + targetName: let target, + versions: let given, + supportedVersions: let supported + ): + "Some of the Swift language versions used in target '\(target)' settings are supported. (given: \(given), supported: \(supported))" + } + } +} diff --git a/Tests/XCBuildSupportTests/PIFBuilderTests.swift b/Tests/XCBuildSupportTests/PIFBuilderTests.swift index 8c4cc22bc35..78883d9e7c5 100644 --- a/Tests/XCBuildSupportTests/PIFBuilderTests.swift +++ b/Tests/XCBuildSupportTests/PIFBuilderTests.swift @@ -2915,7 +2915,8 @@ class PIFBuilderTests: XCTestCase { #endif let fs = InMemoryFileSystem( emptyFiles: - "/Foo/Sources/foo/main.swift" + "/Foo/Sources/foo/main.swift", + "/Foo/Sources/bar/main.swift" ) let observability = ObservabilitySystem.makeForTesting() @@ -2929,6 +2930,13 @@ class PIFBuilderTests: XCTestCase { swiftLanguageVersions: [.v4_2, .v5], targets: [ .init(name: "foo", dependencies: []), + .init(name: "bar", dependencies: [], settings: [ + .init( + tool: .swift, + kind: .swiftLanguageVersion(.v4_2), + condition: .init(platformNames: ["linux"]) + ), + ]), ] ), ], @@ -2938,7 +2946,7 @@ class PIFBuilderTests: XCTestCase { let builder = PIFBuilder( graph: graph, - parameters: .mock(supportedSwiftVersions: [.v4_2]), + parameters: .mock(supportedSwiftVersions: [.v4_2, .v5]), fileSystem: fs, observabilityScope: observability.topScope ) @@ -2951,13 +2959,94 @@ class PIFBuilderTests: XCTestCase { project.checkTarget("PACKAGE-PRODUCT:foo") { target in target.checkBuildConfiguration("Debug") { configuration in configuration.checkBuildSettings { settings in - XCTAssertEqual(settings[.SWIFT_VERSION], "4.2") + XCTAssertEqual(settings[.SWIFT_VERSION], "5") + } + } + } + + project.checkTarget("PACKAGE-PRODUCT:bar") { target in + target.checkBuildConfiguration("Debug") { configuration in + configuration.checkBuildSettings { settings in + XCTAssertEqual(settings[.SWIFT_VERSION], "5") + XCTAssertEqual(settings[.SWIFT_VERSION, for: .linux], "4.2") } } } } } } + + func testPerTargetSwiftVersions() throws { + #if !os(macOS) + try XCTSkipIf(true, "test is only supported on macOS") + #endif + let fs = InMemoryFileSystem( + emptyFiles: + "/Foo/Sources/foo/main.swift", + "/Foo/Sources/bar/main.swift", + "/Foo/Sources/baz/main.swift" + ) + + let observability = ObservabilitySystem.makeForTesting() + let graph = try loadModulesGraph( + fileSystem: fs, + manifests: [ + Manifest.createRootManifest( + displayName: "Foo", + path: "/Foo", + toolsVersion: .v5_3, + swiftLanguageVersions: [.v4_2, .v5], + targets: [ + .init(name: "foo", dependencies: [], settings: [ + .init( + tool: .swift, + kind: .swiftLanguageVersion(.v4_2) + ), + ]), + .init(name: "bar", dependencies: [], settings: [ + .init( + tool: .swift, + kind: .swiftLanguageVersion(.v6) + ), + ]), + .init(name: "baz", dependencies: [], settings: [ + .init( + tool: .swift, + kind: .swiftLanguageVersion(.v3), + condition: .init(platformNames: ["linux"]) + ), + .init( + tool: .swift, + kind: .swiftLanguageVersion(.v4_2), + condition: .init(platformNames: ["macOS"]) + ), + ]), + ] + ), + ], + shouldCreateMultipleTestProducts: true, + observabilityScope: observability.topScope + ) + + let builder = PIFBuilder( + graph: graph, + parameters: .mock(supportedSwiftVersions: [.v4_2, .v5]), + fileSystem: fs, + observabilityScope: observability.topScope + ) + let _ = try builder.construct() + + testDiagnostics(observability.diagnostics) { result in + result.check( + diagnostic: "Some of the Swift language versions used in target 'bar' settings are supported. (given: [6], supported: [4.2, 5])", + severity: .error + ) + result.check( + diagnostic: "Some of the Swift language versions used in target 'baz' settings are supported. (given: [3], supported: [4.2, 5])", + severity: .error + ) + } + } } extension PIFBuilderParameters { From e6d2128b42772563de4f4cf115475e80756cc6d8 Mon Sep 17 00:00:00 2001 From: stackotter Date: Mon, 12 Feb 2024 00:36:42 +1000 Subject: [PATCH 156/159] Initial implementation of target dependencies on same-package products (no cycle checks or tests) --- .../Build/BuildPlan/BuildPlan+Product.swift | 3 ++ .../Utilities/MermaidPackageSerializer.swift | 8 ++++++ .../PackageDescriptionSerialization.swift | 1 + ...geDescriptionSerializationConversion.swift | 5 ++++ Sources/PackageDescription/Target.swift | 21 ++++++++++++++ .../PackageGraph/ModulesGraph+Loading.swift | 20 +++++++++++-- .../PackageLoading/ManifestJSONParser.swift | 2 ++ .../ManifestLoader+Validation.swift | 3 ++ Sources/PackageLoading/PackageBuilder.swift | 28 +++++++++++++------ Sources/PackageModel/Manifest/Manifest.swift | 3 ++ .../Manifest/TargetDescription.swift | 12 +++++++- .../ManifestSourceGeneration.swift | 7 +++++ Sources/PackageModel/Target/Target.swift | 27 ++++++++++++++++++ .../Plugins/PluginInvocation.swift | 10 +++++++ 14 files changed, 139 insertions(+), 11 deletions(-) diff --git a/Sources/Build/BuildPlan/BuildPlan+Product.swift b/Sources/Build/BuildPlan/BuildPlan+Product.swift index c1731786e1d..f671a6fb367 100644 --- a/Sources/Build/BuildPlan/BuildPlan+Product.swift +++ b/Sources/Build/BuildPlan/BuildPlan+Product.swift @@ -148,6 +148,9 @@ extension BuildPlan { switch $0 { case .product: return nil + case .innerProduct: + // TODO: Does this just mean that we can't @testable inner products? (seems fair enough?) + return nil case .target(let target, _): return target } diff --git a/Sources/Commands/Utilities/MermaidPackageSerializer.swift b/Sources/Commands/Utilities/MermaidPackageSerializer.swift index 06bad495f72..d2153f97eab 100644 --- a/Sources/Commands/Utilities/MermaidPackageSerializer.swift +++ b/Sources/Commands/Utilities/MermaidPackageSerializer.swift @@ -115,6 +115,14 @@ extension MermaidPackageSerializer.Node { border: .hexagon, subgraph: product.package ) + case let .innerProduct(product, _): + // TODO: Do we need a subgraph here? + self.init( + id: product.name, + title: product.name, + border: .hexagon, + subgraph: nil + ) case let .target(target, _): self.init(target: target) } diff --git a/Sources/PackageDescription/PackageDescriptionSerialization.swift b/Sources/PackageDescription/PackageDescriptionSerialization.swift index ad852b9d859..ecc3808bb21 100644 --- a/Sources/PackageDescription/PackageDescriptionSerialization.swift +++ b/Sources/PackageDescription/PackageDescriptionSerialization.swift @@ -158,6 +158,7 @@ enum Serialization { case target(name: String, condition: Condition?) case product(name: String, package: String?, moduleAliases: [String: String]?, condition: Condition?) + case innerProduct(name: String, condition: Condition?) case byName(name: String, condition: Condition?) } diff --git a/Sources/PackageDescription/PackageDescriptionSerializationConversion.swift b/Sources/PackageDescription/PackageDescriptionSerializationConversion.swift index e85d9c0cd1f..5335c1ff799 100644 --- a/Sources/PackageDescription/PackageDescriptionSerializationConversion.swift +++ b/Sources/PackageDescription/PackageDescriptionSerializationConversion.swift @@ -205,6 +205,11 @@ extension Serialization.TargetDependency { moduleAliases: moduleAliases, condition: condition.map { .init($0) } ) + case .innerProductItem(let name, let condition): + self = .innerProduct( + name: name, + condition: condition.map { .init($0) } + ) case .byNameItem(let name, let condition): self = .byName(name: name, condition: condition.map { .init($0) }) } diff --git a/Sources/PackageDescription/Target.swift b/Sources/PackageDescription/Target.swift index ce77847d291..efc5b8c3275 100644 --- a/Sources/PackageDescription/Target.swift +++ b/Sources/PackageDescription/Target.swift @@ -57,6 +57,13 @@ public final class Target { /// - moduleAlias: The module aliases for targets in the product. /// - condition: A condition that limits the application of the target dependency. For example, only apply a dependency for a specific platform. case productItem(name: String, package: String?, moduleAliases: [String: String]?, condition: TargetDependencyCondition?) + /// A dependency on a product in the current package. + /// + /// - Parameters: + /// - name: The name of the product. + /// - moduleAlias: The module aliases for targets in the product. + /// - condition: A condition that limits the application of the target dependency. For example, only apply a dependency for a specific platform. + case innerProductItem(name: String, condition: TargetDependencyCondition?) /// A by-name dependency on either a target or a product. /// /// - Parameters: @@ -1263,6 +1270,20 @@ extension Target.Dependency { return .productItem(name: name, package: package, moduleAliases: nil, condition: nil) } + /// Creates a dependency on a product from the same package. + /// + /// - Parameters: + /// - name: The name of the product. + /// - condition: A condition that limits the application of the target dependency. For example, only apply a + /// dependency for a specific platform. + /// - Returns: A `Target.Dependency` instance. + public static func product( + name: String, + condition: TargetDependencyCondition? = nil + ) -> Target.Dependency { + return .innerProductItem(name: name, condition: condition) + } + /// Creates a dependency that resolves to either a target or a product with the specified name. /// /// - Parameter name: The name of the dependency, either a target or a product. diff --git a/Sources/PackageGraph/ModulesGraph+Loading.swift b/Sources/PackageGraph/ModulesGraph+Loading.swift index e99aef7f964..7d2bf0140e0 100644 --- a/Sources/PackageGraph/ModulesGraph+Loading.swift +++ b/Sources/PackageGraph/ModulesGraph+Loading.swift @@ -396,6 +396,8 @@ private func createResolvedPackages( return .target(targetBuilder, conditions: conditions) case .product: return nil + case .innerProduct: + return nil } } targetBuilder.defaultLocalization = packageBuilder.defaultLocalization @@ -403,13 +405,26 @@ private func createResolvedPackages( } // Create product builders for each product in the package. A product can only contain a target present in the same package. - packageBuilder.products = try package.products.map{ - try ResolvedProductBuilder(product: $0, packageBuilder: packageBuilder, targets: $0.targets.map { + packageBuilder.products = try package.products.map { product in + let productBuilder = try ResolvedProductBuilder(product: product, packageBuilder: packageBuilder, targets: product.targets.map { guard let target = targetMap[$0] else { throw InternalError("unknown target \($0)") } return target }) + for targetBuilder in targetBuilders { + var conditions: [PackageCondition]? + for dependency in targetBuilder.target.dependencies { + if case let .innerProduct(innerProduct, dependencyConditions) = dependency, innerProduct.name == product.name { + conditions = dependencyConditions + } + } + if let conditions = conditions { + // TODO: Check for cycles + targetBuilder.dependencies.append(.product(productBuilder, conditions: conditions)) + } + } + return productBuilder } // add registry metadata if available @@ -1009,6 +1024,7 @@ extension Target { } } } + /// Builder for resolved package. private final class ResolvedPackageBuilder: ResolvedBuilder { /// The package reference. diff --git a/Sources/PackageLoading/ManifestJSONParser.swift b/Sources/PackageLoading/ManifestJSONParser.swift index 564e5991982..722c7b8bf88 100644 --- a/Sources/PackageLoading/ManifestJSONParser.swift +++ b/Sources/PackageLoading/ManifestJSONParser.swift @@ -339,6 +339,8 @@ extension TargetDescription.Dependency { package = try identityResolver.mappedIdentity(for: .plain(packageName)).description } self = .product(name: name, package: package, moduleAliases: moduleAliases, condition: condition.map { .init($0) }) + case .innerProduct(let name, let condition): + self = .innerProduct(name: name, condition: condition.map { .init($0) }) case .byName(let name, let condition): self = .byName(name: name, condition: condition.map { .init($0) }) } diff --git a/Sources/PackageLoading/ManifestLoader+Validation.swift b/Sources/PackageLoading/ManifestLoader+Validation.swift index af215dd5298..6a23d957720 100644 --- a/Sources/PackageLoading/ManifestLoader+Validation.swift +++ b/Sources/PackageLoading/ManifestLoader+Validation.swift @@ -196,6 +196,9 @@ public struct ManifestValidator { validPackages: self.manifest.dependencies )) } + case .innerProduct: + // TODO: Diagnose any possible issues here + break case .byName(let name, _): // Don't diagnose root manifests so we can emit a better diagnostic during package loading. if !self.manifest.packageKind.isRoot && diff --git a/Sources/PackageLoading/PackageBuilder.swift b/Sources/PackageLoading/PackageBuilder.swift index 3a318518e1e..c0d845f7045 100644 --- a/Sources/PackageLoading/PackageBuilder.swift +++ b/Sources/PackageLoading/PackageBuilder.swift @@ -674,18 +674,23 @@ public final class PackageBuilder { // No reference of this target in manifest, i.e. it has no dependencies. guard let target = self.manifest.targetMap[$0.name] else { return [] } // Collect the successors from declared dependencies. - var successors: [PotentialModule] = target.dependencies.compactMap { + var successors: [PotentialModule] = target.dependencies.flatMap { switch $0 { case .target(let name, _): // Since we already checked above that all referenced targets // has to present, we always expect this target to be present in // potentialModules dictionary. - potentialModuleMap[name]! + return [potentialModuleMap[name]!] case .product: - nil + return [] + case .innerProduct(let product, _): + guard let product = self.manifest.products.first(where: { $0.name == product }) else { + return [] + } + return product.targets.compactMap { potentialModuleMap[$0] } case .byName(let name, _): // By name dependency may or may not be a target dependency. - potentialModuleMap[name] + return potentialModuleMap[name].map { [$0] } ?? [] } } // If there are plugin usages, consider them to be dependencies too. @@ -745,6 +750,11 @@ public final class PackageBuilder { .init(name: name, package: package, moduleAliases: moduleAliases), conditions: buildConditions(from: condition) ) + case .innerProduct(let name, let condition): + return .innerProduct( + .init(name: name), + conditions: buildConditions(from: condition) + ) case .byName(let name, let condition): // We don't create an object for targets which have no sources. if emptyModules.contains(name) { return nil } @@ -1651,9 +1661,9 @@ extension Manifest { [target.name] + target.dependencies.compactMap { switch $0 { case .target(let name, _): - name - case .byName, .product: - nil + return name + case .byName, .product, .innerProduct: + return nil } } } @@ -1701,7 +1711,9 @@ extension Target.Dependency { case .target: "target-\(name)" case .product: - "product-\(name)" + return "product-\(name)" + case .innerProduct: + return "innerproduct-\(name)" } } } diff --git a/Sources/PackageModel/Manifest/Manifest.swift b/Sources/PackageModel/Manifest/Manifest.swift index 73adb0802e0..bacd66fa65c 100644 --- a/Sources/PackageModel/Manifest/Manifest.swift +++ b/Sources/PackageModel/Manifest/Manifest.swift @@ -389,6 +389,9 @@ public final class Manifest: Sendable { } else { // < 5.2 registry.unknown.insert(product) } + case .innerProduct: + // All products in the root package are already requested by definition. + break case .byName(let product, _): if self.toolsVersion < .v5_2 { // A by‐name entry might be a product from anywhere. diff --git a/Sources/PackageModel/Manifest/TargetDescription.swift b/Sources/PackageModel/Manifest/TargetDescription.swift index 1c943a5313c..b1b4b570641 100644 --- a/Sources/PackageModel/Manifest/TargetDescription.swift +++ b/Sources/PackageModel/Manifest/TargetDescription.swift @@ -28,6 +28,7 @@ public struct TargetDescription: Hashable, Encodable, Sendable { public enum Dependency: Hashable, Sendable { case target(name: String, condition: PackageConditionDescription?) case product(name: String, package: String?, moduleAliases: [String: String]? = nil, condition: PackageConditionDescription?) + case innerProduct(name: String, condition: PackageConditionDescription?) case byName(name: String, condition: PackageConditionDescription?) public static func target(name: String) -> Dependency { @@ -259,7 +260,7 @@ public struct TargetDescription: Hashable, Encodable, Sendable { extension TargetDescription.Dependency: Codable { private enum CodingKeys: String, CodingKey { - case target, product, byName + case target, product, innerProduct, byName } public func encode(to encoder: Encoder) throws { @@ -275,6 +276,10 @@ extension TargetDescription.Dependency: Codable { try unkeyedContainer.encode(a2) try unkeyedContainer.encode(a3) try unkeyedContainer.encode(a4) + case let .innerProduct(a1, a2): + var unkeyedContainer = container.nestedUnkeyedContainer(forKey: .innerProduct) + try unkeyedContainer.encode(a1) + try unkeyedContainer.encode(a2) case let .byName(a1, a2): var unkeyedContainer = container.nestedUnkeyedContainer(forKey: .byName) try unkeyedContainer.encode(a1) @@ -300,6 +305,11 @@ extension TargetDescription.Dependency: Codable { let a3 = try unkeyedValues.decode([String: String].self) let a4 = try unkeyedValues.decodeIfPresent(PackageConditionDescription.self) self = .product(name: a1, package: a2, moduleAliases: a3, condition: a4) + case .innerProduct: + var unkeyedValues = try values.nestedUnkeyedContainer(forKey: key) + let a1 = try unkeyedValues.decode(String.self) + let a2 = try unkeyedValues.decodeIfPresent(PackageConditionDescription.self) + self = .innerProduct(name: a1, condition: a2) case .byName: var unkeyedValues = try values.nestedUnkeyedContainer(forKey: key) let a1 = try unkeyedValues.decode(String.self) diff --git a/Sources/PackageModel/ManifestSourceGeneration.swift b/Sources/PackageModel/ManifestSourceGeneration.swift index 211c44032ee..e1d68156242 100644 --- a/Sources/PackageModel/ManifestSourceGeneration.swift +++ b/Sources/PackageModel/ManifestSourceGeneration.swift @@ -348,6 +348,13 @@ fileprivate extension SourceCodeFragment { } self.init(enum: "product", subnodes: params) + case .innerProduct(name: let name, condition: let condition): + params.append(SourceCodeFragment(key: "name", string: name)) + if let condition { + params.append(SourceCodeFragment(key: "condition", subnode: SourceCodeFragment(from: condition))) + } + self.init(enum: "product", subnodes: params) + case .byName(name: let name, condition: let condition): if let condition { params.append(SourceCodeFragment(key: "name", string: name)) diff --git a/Sources/PackageModel/Target/Target.swift b/Sources/PackageModel/Target/Target.swift index 95208121df6..00937ed1103 100644 --- a/Sources/PackageModel/Target/Target.swift +++ b/Sources/PackageModel/Target/Target.swift @@ -77,6 +77,17 @@ public class Target: PolymorphicCodableProtocol { } } + /// A reference to a product within the same package as a target dependency. + public struct InnerProductReference: Codable { + /// The name of the product dependency. + public let name: String + + /// Creates an inner product reference instance. + public init(name: String) { + self.name = name + } + } + /// A target dependency to a target or product. public enum Dependency { /// A dependency referencing another target, with conditions. @@ -85,6 +96,9 @@ public class Target: PolymorphicCodableProtocol { /// A dependency referencing a product, with conditions. case product(_ product: ProductReference, conditions: [PackageCondition]) + /// A dependency referencing a product in the same package, with conditions. + case innerProduct(_ product: InnerProductReference, conditions: [PackageCondition]) + /// The target if the dependency is a target dependency. public var target: Target? { if case .target(let target, _) = self { @@ -103,6 +117,15 @@ public class Target: PolymorphicCodableProtocol { } } + /// The inner product reference if the dependency is an inner product dependency. + public var innerProduct: InnerProductReference? { + if case .innerProduct(let product, _) = self { + return product + } else { + return nil + } + } + /// The dependency conditions. public var conditions: [PackageCondition] { switch self { @@ -110,6 +133,8 @@ public class Target: PolymorphicCodableProtocol { return conditions case .product(_, let conditions): return conditions + case .innerProduct(_, let conditions): + return conditions } } @@ -120,6 +145,8 @@ public class Target: PolymorphicCodableProtocol { return target.name case .product(let product, _): return product.name + case .innerProduct(let product, _): + return product.name } } } diff --git a/Sources/SPMBuildCore/Plugins/PluginInvocation.swift b/Sources/SPMBuildCore/Plugins/PluginInvocation.swift index 3ab08cd47d5..7eb97d7c781 100644 --- a/Sources/SPMBuildCore/Plugins/PluginInvocation.swift +++ b/Sources/SPMBuildCore/Plugins/PluginInvocation.swift @@ -684,6 +684,15 @@ public extension PluginTarget { } builtToolName = productRef.name executableOrBinaryTarget = executableTarget + case .innerProduct(let productRef, _): + guard + let product = packageGraph.allProducts.first(where: { $0.name == productRef.name }), + let executableTarget = product.targets.map({ $0.underlying }).executables.spm_only + else { + throw StringError("no product named \(productRef.name)") + } + builtToolName = productRef.name + executableOrBinaryTarget = executableTarget } // For a binary target we create a `vendedTool`. @@ -730,6 +739,7 @@ fileprivate extension Target.Dependency { switch self { case .target(_, let conditions): return conditions case .product(_, let conditions): return conditions + case .innerProduct(_, let conditions): return conditions } } From 423c27cacf5d4adbb2ad6bb258e5e423500d0469 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?furby=E2=84=A2?= Date: Wed, 15 May 2024 12:20:06 -0600 Subject: [PATCH 157/159] Add syntax for @stackotter same deps, fix return. --- Sources/PackageLoading/PackageBuilder.swift | 2 +- Sources/PackageModelSyntax/AddTarget.swift | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Sources/PackageLoading/PackageBuilder.swift b/Sources/PackageLoading/PackageBuilder.swift index c0d845f7045..e107fd24ef4 100644 --- a/Sources/PackageLoading/PackageBuilder.swift +++ b/Sources/PackageLoading/PackageBuilder.swift @@ -1709,7 +1709,7 @@ extension Target.Dependency { fileprivate var nameAndType: String { switch self { case .target: - "target-\(name)" + return "target-\(name)" case .product: return "product-\(name)" case .innerProduct: diff --git a/Sources/PackageModelSyntax/AddTarget.swift b/Sources/PackageModelSyntax/AddTarget.swift index 75bdbad37b5..23585174bf8 100644 --- a/Sources/PackageModelSyntax/AddTarget.swift +++ b/Sources/PackageModelSyntax/AddTarget.swift @@ -329,7 +329,8 @@ fileprivate extension TargetDescription.Dependency { switch self { case .target(name: let name, condition: _), .byName(name: let name, condition: _), - .product(name: let name, package: _, moduleAliases: _, condition: _): + .product(name: let name, package: _, moduleAliases: _, condition: _), + .innerProduct(name: let name, condition: _): name } } From 9769a5b5f818430b6603f9083d9c3408bed3b780 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?furby=E2=84=A2?= Date: Thu, 16 May 2024 02:39:46 -0600 Subject: [PATCH 158/159] Create an ugly fugly hack (need to fill out dependencies). --- .../PackageGraph/Resolution/ResolvedProduct.swift | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Sources/PackageGraph/Resolution/ResolvedProduct.swift b/Sources/PackageGraph/Resolution/ResolvedProduct.swift index 3217ef8d9ea..0ad9a376dea 100644 --- a/Sources/PackageGraph/Resolution/ResolvedProduct.swift +++ b/Sources/PackageGraph/Resolution/ResolvedProduct.swift @@ -75,14 +75,25 @@ public struct ResolvedProduct { assert(product.targets.count == targets.count && product.targets.map(\.name).sorted() == targets.map(\.name).sorted()) self.packageIdentity = packageIdentity self.underlying = product - self.targets = targets + + let (platforms, platformVersionProvider) = Self.computePlatforms(targets: targets) + self.targets = IdentifiableSet(product.targets.map { targ in + ResolvedModule( + packageIdentity: packageIdentity, + underlying: targ, + dependencies: targets.flatMap { $0.dependencies.first }, + defaultLocalization: nil, + supportedPlatforms: platforms, + platformVersionProvider: platformVersionProvider + ) + }) // defaultLocalization is currently shared across the entire package // this may need to be enhanced if / when we support localization per target or product let defaultLocalization = self.targets.first?.defaultLocalization self.defaultLocalization = defaultLocalization - let (platforms, platformVersionProvider) = Self.computePlatforms(targets: targets) + self.supportedPlatforms = platforms self.platformVersionProvider = platformVersionProvider From ef4e3e0c980978819d132872e2fae643f0af5ad7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?furby=E2=84=A2?= Date: Thu, 16 May 2024 14:53:20 -0600 Subject: [PATCH 159/159] Fixup same package product dependencies. --- .../PackageGraph/ModulesGraph+Loading.swift | 36 ++++++++++++++++--- .../Resolution/ResolvedProduct.swift | 15 ++------ 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/Sources/PackageGraph/ModulesGraph+Loading.swift b/Sources/PackageGraph/ModulesGraph+Loading.swift index 7d2bf0140e0..7b431ca4b29 100644 --- a/Sources/PackageGraph/ModulesGraph+Loading.swift +++ b/Sources/PackageGraph/ModulesGraph+Loading.swift @@ -414,14 +414,42 @@ private func createResolvedPackages( }) for targetBuilder in targetBuilders { var conditions: [PackageCondition]? - for dependency in targetBuilder.target.dependencies { - if case let .innerProduct(innerProduct, dependencyConditions) = dependency, innerProduct.name == product.name { + var dependency: Target.Dependency? + for dep in targetBuilder.target.dependencies { + dependency = dep + if case let .innerProduct(innerProduct, dependencyConditions) = dep, innerProduct.name == product.name { conditions = dependencyConditions } } - if let conditions = conditions { + if let conditions = conditions, + let dependency = dependency { // TODO: Check for cycles - targetBuilder.dependencies.append(.product(productBuilder, conditions: conditions)) + let exists = targetBuilder.dependencies.contains { + if case .target(let trgtBuilder, let _) = $0 { + return trgtBuilder.target.name == productBuilder.product.name + } + if case .product(let productBuilder, let _) = $0 { + return productBuilder.product.name == productBuilder.product.name + } + return false + } + if exists { + targetBuilder.dependencies += targetBuilder.dependencies.filter { + if case .target(let trgtBuilder, let _) = $0 { + return trgtBuilder.target.name == productBuilder.product.name + } + if case .product(let prodBuilder, let _) = $0 { + return prodBuilder.product.name == productBuilder.product.name + } + return false + }.map { _ in + return .product(productBuilder, conditions: conditions) + } + } + else + { + targetBuilder.dependencies.append(.product(productBuilder, conditions: conditions)) + } } } return productBuilder diff --git a/Sources/PackageGraph/Resolution/ResolvedProduct.swift b/Sources/PackageGraph/Resolution/ResolvedProduct.swift index 0ad9a376dea..3217ef8d9ea 100644 --- a/Sources/PackageGraph/Resolution/ResolvedProduct.swift +++ b/Sources/PackageGraph/Resolution/ResolvedProduct.swift @@ -75,25 +75,14 @@ public struct ResolvedProduct { assert(product.targets.count == targets.count && product.targets.map(\.name).sorted() == targets.map(\.name).sorted()) self.packageIdentity = packageIdentity self.underlying = product - - let (platforms, platformVersionProvider) = Self.computePlatforms(targets: targets) - self.targets = IdentifiableSet(product.targets.map { targ in - ResolvedModule( - packageIdentity: packageIdentity, - underlying: targ, - dependencies: targets.flatMap { $0.dependencies.first }, - defaultLocalization: nil, - supportedPlatforms: platforms, - platformVersionProvider: platformVersionProvider - ) - }) + self.targets = targets // defaultLocalization is currently shared across the entire package // this may need to be enhanced if / when we support localization per target or product let defaultLocalization = self.targets.first?.defaultLocalization self.defaultLocalization = defaultLocalization - + let (platforms, platformVersionProvider) = Self.computePlatforms(targets: targets) self.supportedPlatforms = platforms self.platformVersionProvider = platformVersionProvider