diff --git a/Examples/package-info/Sources/package-info/example.swift b/Examples/package-info/Sources/package-info/example.swift index 5b64bfc8d9c..5125d46adf9 100644 --- a/Examples/package-info/Sources/package-info/example.swift +++ b/Examples/package-info/Sources/package-info/example.swift @@ -26,7 +26,7 @@ struct Example { let package = try await workspace.loadRootPackage(at: packagePath, observabilityScope: observability.topScope) - let graph = try workspace.loadPackageGraph(rootPath: packagePath, observabilityScope: observability.topScope) + let graph = try await workspace.loadPackageGraph(rootPath: packagePath, observabilityScope: observability.topScope) // EXAMPLES // ======== diff --git a/Sources/Basics/Graph/GraphAlgorithms.swift b/Sources/Basics/Graph/GraphAlgorithms.swift index 6f7d98970a8..a004be63e8b 100644 --- a/Sources/Basics/Graph/GraphAlgorithms.swift +++ b/Sources/Basics/Graph/GraphAlgorithms.swift @@ -55,3 +55,34 @@ public func depthFirstSearch( } } } + +package func asyncDepthFirstSearch( + _ nodes: [T], + successors: (T) async throws -> [T], + onUnique: (T) -> Void, + onDuplicate: (T, T) -> Void +) async 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 await successors(curr) { + stack.append(succ) + } + } + } +} diff --git a/Sources/Basics/Observability.swift b/Sources/Basics/Observability.swift index 8a5afc23a70..da5da068b87 100644 --- a/Sources/Basics/Observability.swift +++ b/Sources/Basics/Observability.swift @@ -265,6 +265,34 @@ extension DiagnosticsEmitterProtocol { } } + /// trap a throwing closure, emitting diagnostics on error and returning boolean representing success + @discardableResult + public func asyncTrap(_ closure: () async throws -> Void) async -> Bool { + do { + try await closure() + return true + } catch Diagnostics.fatalError { + // FIXME: (diagnostics) deprecate this with Diagnostics.fatalError + return false + } catch { + self.emit(error) + return false + } + } + + /// trap a throwing closure, emitting diagnostics on error and returning the value returned by the closure + public func asyncTrap(_ closure: () async throws -> T) async -> T? { + do { + return try await closure() + } catch Diagnostics.fatalError { + // FIXME: (diagnostics) deprecate this with Diagnostics.fatalError + return nil + } catch { + self.emit(error) + return nil + } + } + /// If `underlyingError` is not `nil`, its human-readable description is interpolated with `message`, /// otherwise `message` itself is returned. private func makeMessage(from message: String, underlyingError: Error?) -> String { diff --git a/Sources/CoreCommands/BuildSystemSupport.swift b/Sources/CoreCommands/BuildSystemSupport.swift index 3ea89cf2f49..67119bbb0be 100644 --- a/Sources/CoreCommands/BuildSystemSupport.swift +++ b/Sources/CoreCommands/BuildSystemSupport.swift @@ -29,19 +29,19 @@ private struct NativeBuildSystemFactory: BuildSystemFactory { cacheBuildManifest: Bool, productsBuildParameters: BuildParameters?, toolsBuildParameters: BuildParameters?, - packageGraphLoader: (() throws -> ModulesGraph)?, + packageGraphLoader: (() async throws -> ModulesGraph)?, outputStream: OutputByteStream?, logLevel: Diagnostic.Severity?, observabilityScope: ObservabilityScope? - ) throws -> any BuildSystem { - let rootPackageInfo = try swiftCommandState.getRootPackageInformation() + ) async throws -> any BuildSystem { + let rootPackageInfo = try await swiftCommandState.getRootPackageInformation() let testEntryPointPath = productsBuildParameters?.testingParameters.testProductStyle.explicitlySpecifiedEntryPointPath return try BuildOperation( productsBuildParameters: try productsBuildParameters ?? self.swiftCommandState.productsBuildParameters, toolsBuildParameters: try toolsBuildParameters ?? self.swiftCommandState.toolsBuildParameters, cacheBuildManifest: cacheBuildManifest && self.swiftCommandState.canUseCachedBuildManifest(), packageGraphLoader: packageGraphLoader ?? { - try self.swiftCommandState.loadPackageGraph( + try await self.swiftCommandState.loadPackageGraph( explicitProduct: explicitProduct, traitConfiguration: traitConfiguration, testEntryPointPath: testEntryPointPath diff --git a/Sources/CoreCommands/Options.swift b/Sources/CoreCommands/Options.swift index 8e148178ab0..20f9c09cc4a 100644 --- a/Sources/CoreCommands/Options.swift +++ b/Sources/CoreCommands/Options.swift @@ -587,7 +587,7 @@ public struct TestLibraryOptions: ParsableArguments { /// Whether to enable support for swift-testing. public func enableSwiftTestingLibrarySupport( swiftCommandState: SwiftCommandState - ) throws -> Bool { + ) async throws -> Bool { // Honor the user's explicit command-line selection, if any. if let callerSuppliedValue = explicitlyEnableSwiftTestingLibrarySupport { return callerSuppliedValue @@ -596,13 +596,10 @@ public struct TestLibraryOptions: ParsableArguments { // 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 - ) - } + let rootManifests = await workspace.loadRootManifests( + packages: root.packages, + observabilityScope: swiftCommandState.observabilityScope + ) // Is swift-testing among the dependencies of the package being built? // If so, enable support. @@ -634,13 +631,13 @@ public struct TestLibraryOptions: ParsableArguments { /// Get the set of enabled testing libraries. public func enabledTestingLibraries( swiftCommandState: SwiftCommandState - ) throws -> Set { + ) async throws -> Set { var result = Set() if enableXCTestSupport { result.insert(.xctest) } - if try enableSwiftTestingLibrarySupport(swiftCommandState: swiftCommandState) { + if try await enableSwiftTestingLibrarySupport(swiftCommandState: swiftCommandState) { result.insert(.swiftTesting) } diff --git a/Sources/CoreCommands/SwiftCommandState.swift b/Sources/CoreCommands/SwiftCommandState.swift index c21cc55005c..9eaf717df68 100644 --- a/Sources/CoreCommands/SwiftCommandState.swift +++ b/Sources/CoreCommands/SwiftCommandState.swift @@ -480,16 +480,16 @@ public final class SwiftCommandState { return workspace } - public func getRootPackageInformation() throws -> (dependencies: [PackageIdentity: [PackageIdentity]], targets: [PackageIdentity: [String]]) { + public func getRootPackageInformation() async throws -> ( + dependencies: [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 - ) - } + let rootManifests = await workspace.loadRootManifests( + packages: root.packages, + observabilityScope: self.observabilityScope + ) var identities = [PackageIdentity: [PackageIdentity]]() var targets = [PackageIdentity: [String]]() @@ -587,11 +587,11 @@ public final class SwiftCommandState { } /// Resolve the dependencies. - public func resolve() throws { + public func resolve() async throws { let workspace = try getActiveWorkspace() let root = try getWorkspaceRoot() - try workspace.resolve( + try await workspace.resolve( root: root, forceResolution: false, forceResolvedVersions: options.resolver.forceResolvedVersions, @@ -614,8 +614,8 @@ public final class SwiftCommandState { public func loadPackageGraph( explicitProduct: String? = nil, testEntryPointPath: AbsolutePath? = nil - ) throws -> ModulesGraph { - try self.loadPackageGraph( + ) async throws -> ModulesGraph { + try await self.loadPackageGraph( explicitProduct: explicitProduct, traitConfiguration: nil, testEntryPointPath: testEntryPointPath @@ -632,12 +632,12 @@ public final class SwiftCommandState { explicitProduct: String? = nil, traitConfiguration: TraitConfiguration? = nil, testEntryPointPath: AbsolutePath? = nil - ) throws -> ModulesGraph { + ) async throws -> ModulesGraph { do { let workspace = try getActiveWorkspace() // Fetch and load the package graph. - let graph = try workspace.loadPackageGraph( + let graph = try await workspace.loadPackageGraph( rootInput: getWorkspaceRoot(), explicitProduct: explicitProduct, traitConfiguration: traitConfiguration, diff --git a/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift b/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift index 3fa47caa50b..340ad14e1d1 100644 --- a/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift +++ b/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift @@ -104,11 +104,11 @@ public protocol BuildSystemFactory { cacheBuildManifest: Bool, productsBuildParameters: BuildParameters?, toolsBuildParameters: BuildParameters?, - packageGraphLoader: (() throws -> ModulesGraph)?, + packageGraphLoader: (() async throws -> ModulesGraph)?, outputStream: OutputByteStream?, logLevel: Diagnostic.Severity?, observabilityScope: ObservabilityScope? - ) throws -> any BuildSystem + ) async throws -> any BuildSystem } public struct BuildSystemProvider { @@ -135,11 +135,11 @@ public struct BuildSystemProvider { outputStream: OutputByteStream? = .none, logLevel: Diagnostic.Severity? = .none, observabilityScope: ObservabilityScope? = .none - ) throws -> any BuildSystem { + ) async throws -> any BuildSystem { guard let buildSystemFactory = self.providers[kind] else { throw Errors.buildSystemProviderNotRegistered(kind: kind) } - return try buildSystemFactory.makeBuildSystem( + return try await buildSystemFactory.makeBuildSystem( explicitProduct: explicitProduct, traitConfiguration: traitConfiguration, cacheBuildManifest: cacheBuildManifest, diff --git a/Sources/Workspace/Workspace+Dependencies.swift b/Sources/Workspace/Workspace+Dependencies.swift index 2e99b7d2107..44bfa39283b 100644 --- a/Sources/Workspace/Workspace+Dependencies.swift +++ b/Sources/Workspace/Workspace+Dependencies.swift @@ -58,7 +58,7 @@ extension Workspace { packages: [String] = [], dryRun: Bool = false, observabilityScope: ObservabilityScope - ) throws -> [(PackageReference, Workspace.PackageStateChange)]? { + ) async throws -> [(PackageReference, Workspace.PackageStateChange)]? { let start = DispatchTime.now() self.delegate?.willUpdateDependencies() defer { @@ -70,11 +70,10 @@ extension Workspace { // FIXME: this should not block // Load the root manifests and currently checked out manifests. - let rootManifests = try temp_await { self.loadRootManifests( + let rootManifests = await self.loadRootManifests( packages: root.packages, - observabilityScope: observabilityScope, - completion: $0 - ) } + observabilityScope: observabilityScope + ) let rootManifestsMinimumToolsVersion = rootManifests.values.map(\.toolsVersion).min() ?? ToolsVersion.current let resolvedFileOriginHash = try self.computeResolvedFileOriginHash(root: root) @@ -85,7 +84,7 @@ extension Workspace { dependencyMapper: self.dependencyMapper, observabilityScope: observabilityScope ) - let currentManifests = try self.loadDependencyManifests( + let currentManifests = try await self.loadDependencyManifests( root: graphRoot, observabilityScope: observabilityScope ) @@ -161,7 +160,7 @@ extension Workspace { } // Load the updated manifests. - let updatedDependencyManifests = try self.loadDependencyManifests( + let updatedDependencyManifests = try await self.loadDependencyManifests( root: graphRoot, observabilityScope: observabilityScope ) @@ -198,7 +197,7 @@ extension Workspace { explicitProduct: String?, resolvedFileStrategy: ResolvedFileStrategy, observabilityScope: ObservabilityScope - ) throws -> DependencyManifests { + ) async throws -> DependencyManifests { let start = DispatchTime.now() self.delegate?.willResolveDependencies() defer { @@ -208,19 +207,19 @@ extension Workspace { switch resolvedFileStrategy { case .lockFile: observabilityScope.emit(info: "using '\(self.location.resolvedVersionsFile.basename)' file as lock file") - return try self._resolveBasedOnResolvedVersionsFile( + return try await self._resolveBasedOnResolvedVersionsFile( root: root, explicitProduct: explicitProduct, observabilityScope: observabilityScope ) case .update(let forceResolution): - return try resolveAndUpdateResolvedFile(forceResolution: forceResolution) + return try await resolveAndUpdateResolvedFile(forceResolution: forceResolution) case .bestEffort: guard !self.state.dependencies.hasEditedDependencies() else { - return try resolveAndUpdateResolvedFile(forceResolution: false) + return try await resolveAndUpdateResolvedFile(forceResolution: false) } guard self.fileSystem.exists(self.location.resolvedVersionsFile) else { - return try resolveAndUpdateResolvedFile(forceResolution: false) + return try await resolveAndUpdateResolvedFile(forceResolution: false) } guard let pinsStore = try? self.pinsStore.load(), let storedHash = pinsStore.originHash else { @@ -228,7 +227,7 @@ extension Workspace { .emit( debug: "'\(self.location.resolvedVersionsFile.basename)' origin hash is missing. resolving and updating accordingly" ) - return try resolveAndUpdateResolvedFile(forceResolution: false) + return try await resolveAndUpdateResolvedFile(forceResolution: false) } let currentHash = try self.computeResolvedFileOriginHash(root: root) @@ -237,14 +236,14 @@ extension Workspace { .emit( debug: "'\(self.location.resolvedVersionsFile.basename)' origin hash does do not match manifest dependencies. resolving and updating accordingly" ) - return try resolveAndUpdateResolvedFile(forceResolution: false) + return try await resolveAndUpdateResolvedFile(forceResolution: false) } observabilityScope .emit( debug: "'\(self.location.resolvedVersionsFile.basename)' origin hash matches manifest dependencies, attempting resolution based on this file" ) - let (manifests, precomputationResult) = try self.tryResolveBasedOnResolvedVersionsFile( + let (manifests, precomputationResult) = try await self.tryResolveBasedOnResolvedVersionsFile( root: root, explicitProduct: explicitProduct, observabilityScope: observabilityScope @@ -261,13 +260,13 @@ extension Workspace { .emit( debug: "resolution based on '\(self.location.resolvedVersionsFile.basename)' could not be completed because \(reasonString). resolving and updating accordingly" ) - return try resolveAndUpdateResolvedFile(forceResolution: false) + return try await resolveAndUpdateResolvedFile(forceResolution: false) } } - func resolveAndUpdateResolvedFile(forceResolution: Bool) throws -> DependencyManifests { + func resolveAndUpdateResolvedFile(forceResolution: Bool) async throws -> DependencyManifests { observabilityScope.emit(debug: "resolving and updating '\(self.location.resolvedVersionsFile.basename)'") - return try self.resolveAndUpdateResolvedFile( + return try await self.resolveAndUpdateResolvedFile( root: root, explicitProduct: explicitProduct, forceResolution: forceResolution, @@ -297,8 +296,8 @@ extension Workspace { root: PackageGraphRootInput, explicitProduct: String?, observabilityScope: ObservabilityScope - ) throws -> DependencyManifests { - let (manifests, precomputationResult) = try self.tryResolveBasedOnResolvedVersionsFile( + ) async throws -> DependencyManifests { + let (manifests, precomputationResult) = try await self.tryResolveBasedOnResolvedVersionsFile( root: root, explicitProduct: explicitProduct, observabilityScope: observabilityScope @@ -334,16 +333,15 @@ extension Workspace { root: PackageGraphRootInput, explicitProduct: String?, observabilityScope: ObservabilityScope - ) throws -> (DependencyManifests, ResolutionPrecomputationResult) { + ) async throws -> (DependencyManifests, ResolutionPrecomputationResult) { // Ensure the cache path exists. self.createCacheDirectories(observabilityScope: observabilityScope) // FIXME: this should not block - let rootManifests = try temp_await { self.loadRootManifests( + let rootManifests = await self.loadRootManifests( packages: root.packages, - observabilityScope: observabilityScope, - completion: $0 - ) } + observabilityScope: observabilityScope + ) let graphRoot = PackageGraphRoot( input: root, manifests: rootManifests, @@ -357,7 +355,7 @@ extension Workspace { !observabilityScope.errorsReported else { return try ( - self.loadDependencyManifests( + await self.loadDependencyManifests( root: graphRoot, observabilityScope: observabilityScope ), @@ -464,7 +462,7 @@ extension Workspace { } } - let currentManifests = try self.loadDependencyManifests( + let currentManifests = try await self.loadDependencyManifests( root: graphRoot, automaticallyAddManagedDependencies: true, observabilityScope: observabilityScope @@ -500,17 +498,15 @@ extension Workspace { forceResolution: Bool, constraints: [PackageContainerConstraint], observabilityScope: ObservabilityScope - ) throws -> DependencyManifests { + ) async throws -> DependencyManifests { // Ensure the cache path exists and validate that edited dependencies. self.createCacheDirectories(observabilityScope: observabilityScope) - // FIXME: this should not block // Load the root manifests and currently checked out manifests. - let rootManifests = try temp_await { self.loadRootManifests( + let rootManifests = await self.loadRootManifests( packages: root.packages, - observabilityScope: observabilityScope, - completion: $0 - ) } + observabilityScope: observabilityScope + ) let rootManifestsMinimumToolsVersion = rootManifests.values.map(\.toolsVersion).min() ?? ToolsVersion.current let resolvedFileOriginHash = try self.computeResolvedFileOriginHash(root: root) @@ -522,7 +518,7 @@ extension Workspace { dependencyMapper: self.dependencyMapper, observabilityScope: observabilityScope ) - let currentManifests = try self.loadDependencyManifests( + let currentManifests = try await self.loadDependencyManifests( root: graphRoot, observabilityScope: observabilityScope ) @@ -623,7 +619,7 @@ extension Workspace { } // Update the pinsStore. - let updatedDependencyManifests = try self.loadDependencyManifests( + let updatedDependencyManifests = try await self.loadDependencyManifests( root: graphRoot, observabilityScope: observabilityScope ) diff --git a/Sources/Workspace/Workspace+Editing.swift b/Sources/Workspace/Workspace+Editing.swift index cf429ad2eaa..c1d904a9d2d 100644 --- a/Sources/Workspace/Workspace+Editing.swift +++ b/Sources/Workspace/Workspace+Editing.swift @@ -27,7 +27,7 @@ extension Workspace { revision: Revision? = nil, checkoutBranch: String? = nil, observabilityScope: ObservabilityScope - ) throws { + ) async throws { // Look up the dependency and check if we can edit it. guard let dependency = self.state.dependencies[.plain(packageName)] else { observabilityScope.emit(.dependencyNotFound(packageName: packageName)) @@ -68,16 +68,13 @@ extension Workspace { // a valid manifest with name same as the package we are trying to edit. if fileSystem.exists(destination) { // FIXME: this should not block - let manifest = try temp_await { - self.loadManifest( - packageIdentity: dependency.packageRef.identity, - packageKind: .fileSystem(destination), - packagePath: destination, - packageLocation: dependency.packageRef.locationString, - observabilityScope: observabilityScope, - completion: $0 - ) - } + let manifest = try await self.loadManifest( + packageIdentity: dependency.packageRef.identity, + packageKind: .fileSystem(destination), + packagePath: destination, + packageLocation: dependency.packageRef.locationString, + observabilityScope: observabilityScope + ) guard manifest.displayName == packageName else { return observabilityScope @@ -174,7 +171,7 @@ extension Workspace { forceRemove: Bool, root: PackageGraphRootInput? = nil, observabilityScope: ObservabilityScope - ) throws { + ) async throws { // Compute if we need to force remove. var forceRemove = forceRemove @@ -234,7 +231,7 @@ extension Workspace { // Resolve the dependencies if workspace root is provided. We do this to // ensure the unedited version of this dependency is resolved properly. if let root { - try self._resolve( + try await self._resolve( root: root, explicitProduct: .none, resolvedFileStrategy: .update(forceResolution: false), diff --git a/Sources/Workspace/Workspace+Manifests.swift b/Sources/Workspace/Workspace+Manifests.swift index b1fde04a014..3b10faf3a68 100644 --- a/Sources/Workspace/Workspace+Manifests.swift +++ b/Sources/Workspace/Workspace+Manifests.swift @@ -15,6 +15,7 @@ import struct Basics.Diagnostic import struct Basics.InternalError import class Basics.ObservabilityScope import struct Basics.SwiftVersion +import func Basics.asyncDepthFirstSearch import func Basics.temp_await import func Basics.depthFirstSearch import class Basics.ThreadSafeKeyValueStore @@ -424,7 +425,7 @@ extension Workspace { root: PackageGraphRoot, automaticallyAddManagedDependencies: Bool = false, observabilityScope: ObservabilityScope - ) throws -> DependencyManifests { + ) async throws -> DependencyManifests { let prepopulateManagedDependencies: ([PackageReference]) throws -> Void = { refs in // pre-populate managed dependencies if we are asked to do so (this happens when resolving to a resolved // file) @@ -464,7 +465,7 @@ extension Workspace { } // Validates that all the managed dependencies are still present in the file system. - self.fixManagedDependencies( + await self.fixManagedDependencies( observabilityScope: observabilityScope ) guard !observabilityScope.errorsReported else { @@ -480,11 +481,10 @@ extension Workspace { // Load root dependencies manifests (in parallel) let rootDependencies = root.dependencies.map(\.packageRef) try prepopulateManagedDependencies(rootDependencies) - let rootDependenciesManifests = try temp_await { self.loadManagedManifests( + let rootDependenciesManifests = await self.loadManagedManifests( for: rootDependencies, - observabilityScope: observabilityScope, - completion: $0 - ) } + observabilityScope: observabilityScope + ) let topLevelManifests = root.manifests.merging(rootDependenciesManifests, uniquingKeysWith: { lhs, _ in lhs // prefer roots! @@ -492,26 +492,24 @@ extension Workspace { // optimization: preload first level dependencies manifest (in parallel) let firstLevelDependencies = topLevelManifests.values.map { $0.dependencies.map(\.packageRef) }.flatMap { $0 } - let firstLevelManifests = try temp_await { self.loadManagedManifests( + let firstLevelManifests = await self.loadManagedManifests( for: firstLevelDependencies, - observabilityScope: observabilityScope, - completion: $0 - ) } // FIXME: this should not block + observabilityScope: observabilityScope + ) // 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 - let successorManifests: (KeyedPair) throws -> [KeyedPair] = { pair in + let successorManifests: (KeyedPair) async 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) .filter { !loadedManifests.keys.contains($0.identity) } try prepopulateManagedDependencies(dependenciesToLoad) - let dependenciesManifests = try temp_await { self.loadManagedManifests( + let dependenciesManifests = await self.loadManagedManifests( for: dependenciesToLoad, - observabilityScope: observabilityScope, - completion: $0 - ) } + observabilityScope: observabilityScope + ) dependenciesManifests.forEach { loadedManifests[$0.key] = $0.value } return dependenciesRequired.compactMap { dependency in loadedManifests[dependency.identity].flatMap { @@ -539,7 +537,7 @@ extension Workspace { } var deduplication = [PackageIdentity: Int]() - try depthFirstSearch( + try await asyncDepthFirstSearch( manifestGraphRoots, successors: successorManifests ) { pair in @@ -598,39 +596,35 @@ extension Workspace { /// Loads the given manifests, if it is present in the managed dependencies. private func loadManagedManifests( for packages: [PackageReference], - observabilityScope: ObservabilityScope, - completion: @escaping (Result<[PackageIdentity: Manifest], Error>) -> Void - ) { - let sync = DispatchGroup() - let manifests = ThreadSafeKeyValueStore() - Set(packages).forEach { package in - sync.enter() - self.loadManagedManifest(for: package, observabilityScope: observabilityScope) { manifest in - defer { sync.leave() } - if let manifest { - manifests[package.identity] = manifest + observabilityScope: ObservabilityScope + ) async -> [PackageIdentity: Manifest] { + await withTaskGroup(of: (identity: PackageIdentity, manifest: Manifest?).self) { group in + var result = [PackageIdentity: Manifest]() + + for package in Set(packages) { + group.addTask { + await (package.identity, self.loadManagedManifest(for: package, observabilityScope: observabilityScope)) } } - } - sync.notify(queue: .sharedConcurrent) { - completion(.success(manifests.get())) + return await group.reduce(into: [:]) { dictionary, taskResult in + dictionary[taskResult.identity] = taskResult.manifest + } } } /// Loads the given manifest, if it is present in the managed dependencies. private func loadManagedManifest( for package: PackageReference, - observabilityScope: ObservabilityScope, - completion: @escaping (Manifest?) -> Void - ) { + observabilityScope: ObservabilityScope + ) async -> Manifest? { // Check if this dependency is available. // we also compare the location as this function may attempt to load // dependencies that have the same identity but from a different location // which is an error case we diagnose an report about in the GraphLoading part which // is prepared to handle the case where not all manifest are available guard let managedDependency = self.state.dependencies[comparingLocation: package] else { - return completion(.none) + return nil } // Get the path of the package. @@ -658,7 +652,7 @@ extension Workspace { libraryPath: path, version: version ) - return completion(manifest) + return manifest case .custom(let availableVersion, _): packageKind = managedDependency.packageRef.kind packageVersion = availableVersion @@ -684,7 +678,7 @@ extension Workspace { } // Load and return the manifest. - self.loadManifest( + return try? await self.loadManifest( packageIdentity: managedDependency.packageRef.identity, packageKind: packageKind, packagePath: packagePath, @@ -692,10 +686,7 @@ extension Workspace { packageVersion: packageVersion, fileSystem: fileSystem, observabilityScope: observabilityScope - ) { result in - // error is added to diagnostics in the function above - completion(try? result.get()) - } + ) } /// Load the manifest at a given path. @@ -708,9 +699,8 @@ extension Workspace { packageLocation: String, packageVersion: Version? = nil, fileSystem: FileSystem? = nil, - observabilityScope: ObservabilityScope, - completion: @escaping (Result) -> Void - ) { + observabilityScope: ObservabilityScope + ) async throws -> Manifest { let fileSystem = fileSystem ?? self.fileSystem // Load the manifest, bracketed by the calls to the delegate callbacks. @@ -729,61 +719,64 @@ extension Workspace { var manifestLoadingDiagnostics = [Diagnostic]() let start = DispatchTime.now() - self.manifestLoader.load( - packagePath: packagePath, - packageIdentity: packageIdentity, - packageKind: packageKind, - packageLocation: packageLocation, - packageVersion: packageVersion.map { (version: $0, revision: nil) }, - currentToolsVersion: self.currentToolsVersion, - identityResolver: self.identityResolver, - dependencyMapper: self.dependencyMapper, - fileSystem: fileSystem, - observabilityScope: manifestLoadingScope, - delegateQueue: .sharedConcurrent, - callbackQueue: .sharedConcurrent - ) { result in + let result: Result + do { + let manifest = try await self.manifestLoader.load( + packagePath: packagePath, + packageIdentity: packageIdentity, + packageKind: packageKind, + packageLocation: packageLocation, + packageVersion: packageVersion.map { (version: $0, revision: nil) }, + currentToolsVersion: self.currentToolsVersion, + identityResolver: self.identityResolver, + dependencyMapper: self.dependencyMapper, + fileSystem: fileSystem, + observabilityScope: manifestLoadingScope, + delegateQueue: .sharedConcurrent, + callbackQueue: .sharedConcurrent + ) let duration = start.distance(to: .now()) - var result = result - switch result { - case .failure(let error): - manifestLoadingDiagnostics.append(.error(error)) - self.delegate?.didLoadManifest( - packageIdentity: packageIdentity, - packagePath: packagePath, - url: packageLocation, - version: packageVersion, - packageKind: packageKind, - manifest: nil, - diagnostics: manifestLoadingDiagnostics, - duration: duration - ) - case .success(let manifest): - let validator = ManifestValidator( - manifest: manifest, - sourceControlValidator: self.repositoryManager, - fileSystem: self.fileSystem - ) - let validationIssues = validator.validate() - if !validationIssues.isEmpty { - // Diagnostics.fatalError indicates that a more specific diagnostic has already been added. - result = .failure(Diagnostics.fatalError) - manifestLoadingDiagnostics.append(contentsOf: validationIssues) - } - self.delegate?.didLoadManifest( - packageIdentity: packageIdentity, - packagePath: packagePath, - url: packageLocation, - version: packageVersion, - packageKind: packageKind, - manifest: manifest, - diagnostics: manifestLoadingDiagnostics, - duration: duration - ) + let validator = ManifestValidator( + manifest: manifest, + sourceControlValidator: self.repositoryManager, + fileSystem: self.fileSystem + ) + let validationIssues = validator.validate() + if !validationIssues.isEmpty { + // Diagnostics.fatalError indicates that a more specific diagnostic has already been added. + result = .failure(Diagnostics.fatalError) + manifestLoadingDiagnostics.append(contentsOf: validationIssues) + } else { + result = .success(manifest) } - manifestLoadingScope.emit(manifestLoadingDiagnostics) - completion(result) + self.delegate?.didLoadManifest( + packageIdentity: packageIdentity, + packagePath: packagePath, + url: packageLocation, + version: packageVersion, + packageKind: packageKind, + manifest: manifest, + diagnostics: manifestLoadingDiagnostics, + duration: duration + ) + } catch { + let duration = start.distance(to: .now()) + manifestLoadingDiagnostics.append(.error(error)) + self.delegate?.didLoadManifest( + packageIdentity: packageIdentity, + packagePath: packagePath, + url: packageLocation, + version: packageVersion, + packageKind: packageKind, + manifest: nil, + diagnostics: manifestLoadingDiagnostics, + duration: duration + ) + result = .failure(error) } + + manifestLoadingScope.emit(manifestLoadingDiagnostics) + return try result.get() } /// Validates that all the edited dependencies are still present in the file system. @@ -792,7 +785,7 @@ extension Workspace { /// fallback on the original checkout. private func fixManagedDependencies( observabilityScope: ObservabilityScope - ) { + ) async { // 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() @@ -801,10 +794,10 @@ extension Workspace { // Make a copy of dependencies as we might mutate them in the for loop. let allDependencies = Array(self.state.dependencies) for dependency in allDependencies { - observabilityScope.makeChildScope( + await observabilityScope.makeChildScope( description: "copying managed dependencies", metadata: dependency.packageRef.diagnosticsMetadata - ).trap { + ).asyncTrap { // If the dependency is present, we're done. let dependencyPath = self.path(to: dependency) if fileSystem.isDirectory(dependencyPath) { @@ -861,7 +854,7 @@ 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( + try await self.unedit( dependency: dependency, forceRemove: true, observabilityScope: observabilityScope diff --git a/Sources/Workspace/Workspace.swift b/Sources/Workspace/Workspace.swift index 92e8728c725..4836711c05c 100644 --- a/Sources/Workspace/Workspace.swift +++ b/Sources/Workspace/Workspace.swift @@ -612,9 +612,9 @@ extension Workspace { revision: Revision? = nil, checkoutBranch: String? = nil, observabilityScope: ObservabilityScope - ) { + ) async { do { - try self._edit( + try await self._edit( packageName: packageName, path: path, revision: revision, @@ -642,7 +642,7 @@ extension Workspace { forceRemove: Bool, root: PackageGraphRootInput, observabilityScope: ObservabilityScope - ) throws { + ) async throws { guard let dependency = self.state.dependencies[.plain(packageName)] else { observabilityScope.emit(.dependencyNotFound(packageName: packageName)) return @@ -653,7 +653,7 @@ extension Workspace { metadata: dependency.packageRef.diagnosticsMetadata ) - try self.unedit( + try await self.unedit( dependency: dependency, forceRemove: forceRemove, root: root, @@ -673,8 +673,8 @@ extension Workspace { forceResolution: Bool = false, forceResolvedVersions: Bool = false, observabilityScope: ObservabilityScope - ) throws { - try self._resolve( + ) async throws { + try await self._resolve( root: root, explicitProduct: explicitProduct, resolvedFileStrategy: forceResolvedVersions ? .lockFile : forceResolution ? .update(forceResolution: true) : @@ -703,7 +703,7 @@ extension Workspace { branch: String? = nil, revision: String? = nil, observabilityScope: ObservabilityScope - ) throws { + ) async throws { // Look up the dependency and check if we can pin it. guard let dependency = self.state.dependencies[.plain(packageName)] else { throw StringError("dependency '\(packageName)' was not found") @@ -748,7 +748,7 @@ extension Workspace { ) // Run the resolution. - try self.resolveAndUpdateResolvedFile( + try await self.resolveAndUpdateResolvedFile( root: root, forceResolution: false, constraints: [constraint], @@ -763,8 +763,8 @@ extension Workspace { public func resolveBasedOnResolvedVersionsFile( root: PackageGraphRootInput, observabilityScope: ObservabilityScope - ) throws { - try self._resolveBasedOnResolvedVersionsFile( + ) async throws { + try await self._resolveBasedOnResolvedVersionsFile( root: root, explicitProduct: .none, observabilityScope: observabilityScope @@ -872,8 +872,8 @@ extension Workspace { packages: [String] = [], dryRun: Bool = false, observabilityScope: ObservabilityScope - ) throws -> [(PackageReference, Workspace.PackageStateChange)]? { - try self._updateDependencies( + ) async throws -> [(PackageReference, Workspace.PackageStateChange)]? { + try await self._updateDependencies( root: root, packages: packages, dryRun: dryRun, @@ -890,8 +890,8 @@ extension Workspace { testEntryPointPath: AbsolutePath? = nil, expectedSigningEntities: [PackageIdentity: RegistryReleaseMetadata.SigningEntity] = [:], observabilityScope: ObservabilityScope - ) throws -> ModulesGraph { - try self.loadPackageGraph( + ) async throws -> ModulesGraph { + try await self.loadPackageGraph( rootInput: root, explicitProduct: explicitProduct, traitConfiguration: nil, @@ -913,7 +913,7 @@ extension Workspace { testEntryPointPath: AbsolutePath? = nil, expectedSigningEntities: [PackageIdentity: RegistryReleaseMetadata.SigningEntity] = [:], observabilityScope: ObservabilityScope - ) throws -> ModulesGraph { + ) async throws -> ModulesGraph { let start = DispatchTime.now() self.delegate?.willLoadGraph() defer { @@ -927,7 +927,7 @@ extension Workspace { try self.state.reload() // Perform dependency resolution, if required. - let manifests = try self._resolve( + let manifests = try await self._resolve( root: root, explicitProduct: explicitProduct, resolvedFileStrategy: forceResolvedVersions ? .lockFile : .bestEffort, @@ -974,8 +974,8 @@ extension Workspace { rootPath: AbsolutePath, explicitProduct: String? = nil, observabilityScope: ObservabilityScope - ) throws -> ModulesGraph { - try self.loadPackageGraph( + ) async throws -> ModulesGraph { + try await self.loadPackageGraph( rootPath: rootPath, explicitProduct: explicitProduct, traitConfiguration: nil, @@ -989,8 +989,8 @@ extension Workspace { explicitProduct: String? = nil, traitConfiguration: TraitConfiguration? = nil, observabilityScope: ObservabilityScope - ) throws -> ModulesGraph { - try self.loadPackageGraph( + ) async throws -> ModulesGraph { + try await self.loadPackageGraph( rootInput: PackageGraphRootInput(packages: [rootPath]), explicitProduct: explicitProduct, traitConfiguration: traitConfiguration, @@ -1002,54 +1002,37 @@ extension Workspace { public func loadRootManifests( packages: [AbsolutePath], observabilityScope: ObservabilityScope - ) async throws -> [AbsolutePath: Manifest] { - try await withCheckedThrowingContinuation { continuation in - self.loadRootManifests(packages: packages, observabilityScope: observabilityScope) { result in - continuation.resume(with: result) + ) async -> [AbsolutePath: Manifest] { + let rootManifests = await withTaskGroup(of: (path: AbsolutePath, manifest: Manifest?).self) { group in + for package in Set(packages) { + // TODO: this does not use the identity resolver which is probably fine since its the root packages + group.addTask { + (package, try? await self.loadManifest( + packageIdentity: PackageIdentity(path: package), + packageKind: .root(package), + packagePath: package, + packageLocation: package.pathString, + observabilityScope: observabilityScope + )) + } } - } - } - /// Loads and returns manifests at the given paths. - @available(*, noasync, message: "Use the async alternative") - public func loadRootManifests( - packages: [AbsolutePath], - observabilityScope: ObservabilityScope, - completion: @escaping (Result<[AbsolutePath: Manifest], Error>) -> Void - ) { - let lock = NSLock() - let sync = DispatchGroup() - var rootManifests = [AbsolutePath: Manifest]() - Set(packages).forEach { package in - sync.enter() - // TODO: this does not use the identity resolver which is probably fine since its the root packages - self.loadManifest( - packageIdentity: PackageIdentity(path: package), - packageKind: .root(package), - packagePath: package, - packageLocation: package.pathString, - observabilityScope: observabilityScope - ) { result in - defer { sync.leave() } - if case .success(let manifest) = result { - lock.withLock { - rootManifests[package] = manifest - } - } + return await group.reduce(into: [:]) { + dictionary, + taskResult in dictionary[taskResult.path] = taskResult.manifest } } - sync.notify(queue: .sharedConcurrent) { - // Check for duplicate root packages. - let duplicateRoots = rootManifests.values.spm_findDuplicateElements(by: \.displayName) - if !duplicateRoots.isEmpty { - let name = duplicateRoots[0][0].displayName - observabilityScope.emit(error: "found multiple top-level packages named '\(name)'") - return completion(.success([:])) - } - completion(.success(rootManifests)) + // Check for duplicate root packages. + let duplicateRoots = rootManifests.values.spm_findDuplicateElements(by: \.displayName) + if !duplicateRoots.isEmpty { + let name = duplicateRoots[0][0].displayName + observabilityScope.emit(error: "found multiple top-level packages named '\(name)'") + return [:] } + + return rootManifests } /// Loads and returns manifest at the given path. @@ -1057,105 +1040,73 @@ extension Workspace { at path: AbsolutePath, observabilityScope: ObservabilityScope ) async throws -> Manifest { - try await withCheckedThrowingContinuation { continuation in - self.loadRootManifest(at: path, observabilityScope: observabilityScope) { result in - continuation.resume(with: result) - } + let manifests = await self.loadRootManifests(packages: [path], observabilityScope: observabilityScope) + // normally, we call loadRootManifests which attempts to load any manifest it can and report errors via + // diagnostics + // in this case, we want to load a specific manifest, so if the diagnostics contains an error we want to + // throw + guard !observabilityScope.errorsReported else { + throw Diagnostics.fatalError } - } - - /// Loads and returns manifest at the given path. - public func loadRootManifest( - at path: AbsolutePath, - observabilityScope: ObservabilityScope, - completion: @escaping (Result) -> Void - ) { - self.loadRootManifests(packages: [path], observabilityScope: observabilityScope) { result in - completion(result.tryMap { - // normally, we call loadRootManifests which attempts to load any manifest it can and report errors via - // diagnostics - // in this case, we want to load a specific manifest, so if the diagnostics contains an error we want to - // throw - guard !observabilityScope.errorsReported else { - throw Diagnostics.fatalError - } - guard let manifest = $0[path] else { - throw InternalError("Unknown manifest for '\(path)'") - } - return manifest - }) + guard let manifest = manifests[path] else { + throw InternalError("Unknown manifest for '\(path)'") } + return manifest } /// Loads root package public func loadRootPackage(at path: AbsolutePath, observabilityScope: ObservabilityScope) async throws -> Package { - try await withCheckedThrowingContinuation { continuation in - self.loadRootPackage(at: path, observabilityScope: observabilityScope) { result in - continuation.resume(with: result) + let manifest = try await self.loadRootManifest(at: path, observabilityScope: observabilityScope) + let identity = try self.identityResolver.resolveIdentity(for: manifest.packageKind) + + // radar/82263304 + // compute binary artifacts for the sake of constructing a project model + // note this does not actually download remote artifacts and as such does not have the artifact's type + // or path + let binaryArtifacts = try manifest.targets.filter { $0.type == .binary } + .reduce(into: [String: BinaryArtifact]()) { partial, target in + if let path = target.path { + let artifactPath = try manifest.path.parentDirectory + .appending(RelativePath(validating: path)) + guard let (_, artifactKind) = try BinaryArtifactsManager.deriveBinaryArtifact( + fileSystem: self.fileSystem, + path: artifactPath, + observabilityScope: observabilityScope + ) else { + throw StringError("\(artifactPath) does not contain binary artifact") + } + partial[target.name] = BinaryArtifact( + kind: artifactKind, + originURL: .none, + path: artifactPath + ) + } else if let url = target.url.flatMap(URL.init(string:)) { + let fakePath = try manifest.path.parentDirectory.appending(components: "remote", "archive") + .appending(RelativePath(validating: url.lastPathComponent)) + partial[target.name] = BinaryArtifact( + kind: .unknown, + originURL: url.absoluteString, + path: fakePath + ) + } else { + throw InternalError("a binary target should have either a path or a URL and a checksum") + } } - } - } - /// Loads root package - public func loadRootPackage( - at path: AbsolutePath, - observabilityScope: ObservabilityScope, - completion: @escaping (Result) -> Void - ) { - self.loadRootManifest(at: path, observabilityScope: observabilityScope) { result in - let result = result.tryMap { manifest -> Package in - let identity = try self.identityResolver.resolveIdentity(for: manifest.packageKind) - - // radar/82263304 - // compute binary artifacts for the sake of constructing a project model - // note this does not actually download remote artifacts and as such does not have the artifact's type - // or path - let binaryArtifacts = try manifest.targets.filter { $0.type == .binary } - .reduce(into: [String: BinaryArtifact]()) { partial, target in - if let path = target.path { - let artifactPath = try manifest.path.parentDirectory - .appending(RelativePath(validating: path)) - guard let (_, artifactKind) = try BinaryArtifactsManager.deriveBinaryArtifact( - fileSystem: self.fileSystem, - path: artifactPath, - observabilityScope: observabilityScope - ) else { - throw StringError("\(artifactPath) does not contain binary artifact") - } - partial[target.name] = BinaryArtifact( - kind: artifactKind, - originURL: .none, - path: artifactPath - ) - } else if let url = target.url.flatMap(URL.init(string:)) { - let fakePath = try manifest.path.parentDirectory.appending(components: "remote", "archive") - .appending(RelativePath(validating: url.lastPathComponent)) - partial[target.name] = BinaryArtifact( - kind: .unknown, - originURL: url.absoluteString, - path: fakePath - ) - } else { - throw InternalError("a binary target should have either a path or a URL and a checksum") - } - } + let builder = PackageBuilder( + identity: identity, + manifest: manifest, + productFilter: .everything, + path: path, + additionalFileRules: [], + binaryArtifacts: binaryArtifacts, + fileSystem: self.fileSystem, + observabilityScope: observabilityScope, + // For now we enable all traits + enabledTraits: Set(manifest.traits.map { $0.name }) + ) + return try builder.construct() - let builder = PackageBuilder( - identity: identity, - manifest: manifest, - productFilter: .everything, - path: path, - additionalFileRules: [], - binaryArtifacts: binaryArtifacts, - fileSystem: self.fileSystem, - observabilityScope: observabilityScope, - // For now we enable all traits - enabledTraits: Set(manifest.traits.map { $0.name }) - ) - return try builder.construct() - } - completion(result) - } } public func loadPluginImports( @@ -1188,56 +1139,40 @@ extension Workspace { return importList } - public func loadPackage( - with identity: PackageIdentity, - packageGraph: ModulesGraph, - observabilityScope: ObservabilityScope - ) async throws -> Package { - try await safe_async { - self.loadPackage(with: identity, packageGraph: packageGraph, observabilityScope: observabilityScope, completion: $0) - } - } - /// Loads a single package in the context of a previously loaded graph. This can be useful for incremental loading /// in a longer-lived program, like an IDE. - @available(*, noasync, message: "Use the async alternative") public func loadPackage( with identity: PackageIdentity, packageGraph: ModulesGraph, - observabilityScope: ObservabilityScope, - completion: @escaping (Result) -> Void - ) { + observabilityScope: ObservabilityScope + ) async throws -> Package { guard let previousPackage = packageGraph.package(for: identity) else { - return completion(.failure(StringError("could not find package with identity \(identity)"))) + throw StringError("could not find package with identity \(identity)") } - self.loadManifest( + let manifest = try await self.loadManifest( packageIdentity: identity, packageKind: previousPackage.underlying.manifest.packageKind, packagePath: previousPackage.path, packageLocation: previousPackage.underlying.manifest.packageLocation, observabilityScope: observabilityScope - ) { result in - let result = result.tryMap { manifest -> Package in - let builder = PackageBuilder( - identity: identity, - manifest: manifest, - productFilter: .everything, - // TODO: this will not be correct when reloading a transitive dependencies if `ENABLE_TARGET_BASED_DEPENDENCY_RESOLUTION` is enabled - path: previousPackage.path, - additionalFileRules: self.configuration.additionalFileRules, - binaryArtifacts: packageGraph.binaryArtifacts[identity] ?? [:], - shouldCreateMultipleTestProducts: self.configuration.shouldCreateMultipleTestProducts, - createREPLProduct: self.configuration.createREPLProduct, - fileSystem: self.fileSystem, - observabilityScope: observabilityScope, - // For now we enable all traits - enabledTraits: Set(manifest.traits.map { $0.name }) - ) - return try builder.construct() - } - completion(result) - } + ) + let builder = PackageBuilder( + identity: identity, + manifest: manifest, + productFilter: .everything, + // TODO: this will not be correct when reloading a transitive dependencies if `ENABLE_TARGET_BASED_DEPENDENCY_RESOLUTION` is enabled + path: previousPackage.path, + additionalFileRules: self.configuration.additionalFileRules, + binaryArtifacts: packageGraph.binaryArtifacts[identity] ?? [:], + shouldCreateMultipleTestProducts: self.configuration.shouldCreateMultipleTestProducts, + createREPLProduct: self.configuration.createREPLProduct, + fileSystem: self.fileSystem, + observabilityScope: observabilityScope, + // For now we enable all traits + enabledTraits: Set(manifest.traits.map { $0.name }) + ) + return try builder.construct() } public func acceptIdentityChange( diff --git a/Sources/_InternalTestSupport/MockWorkspace.swift b/Sources/_InternalTestSupport/MockWorkspace.swift index a70725f9b84..7e2fc9bbcc0 100644 --- a/Sources/_InternalTestSupport/MockWorkspace.swift +++ b/Sources/_InternalTestSupport/MockWorkspace.swift @@ -374,11 +374,11 @@ public final class MockWorkspace { revision: Revision? = nil, checkoutBranch: String? = nil, _ result: ([Basics.Diagnostic]) -> Void - ) { + ) async { let observability = ObservabilitySystem.makeForTesting() - observability.topScope.trap { + await observability.topScope.asyncTrap { let ws = try self.getOrCreateWorkspace() - ws.edit( + await ws.edit( packageName: packageName, path: path, revision: revision, @@ -394,12 +394,12 @@ public final class MockWorkspace { roots: [String], forceRemove: Bool = false, _ result: ([Basics.Diagnostic]) -> Void - ) { + ) async { let observability = ObservabilitySystem.makeForTesting() - observability.topScope.trap { + await observability.topScope.asyncTrap { let rootInput = PackageGraphRootInput(packages: try rootPaths(for: roots)) let ws = try self.getOrCreateWorkspace() - try ws.unedit( + try await ws.unedit( packageName: packageName, forceRemove: forceRemove, root: rootInput, @@ -409,12 +409,17 @@ public final class MockWorkspace { result(observability.diagnostics) } - public func checkResolve(pkg: String, roots: [String], version: TSCUtility.Version, _ result: ([Basics.Diagnostic]) -> Void) { + public func checkResolve( + pkg: String, + roots: [String], + version: TSCUtility.Version, + _ result: ([Basics.Diagnostic]) -> Void + ) async { let observability = ObservabilitySystem.makeForTesting() - observability.topScope.trap { + await observability.topScope.asyncTrap { let rootInput = PackageGraphRootInput(packages: try rootPaths(for: roots)) let workspace = try self.getOrCreateWorkspace() - try workspace.resolve(packageName: pkg, root: rootInput, version: version, branch: nil, revision: nil, observabilityScope: observability.topScope) + try await workspace.resolve(packageName: pkg, root: rootInput, version: version, branch: nil, revision: nil, observabilityScope: observability.topScope) } result(observability.diagnostics) } @@ -442,17 +447,17 @@ public final class MockWorkspace { deps: [MockDependency] = [], packages: [String] = [], _ result: ([Basics.Diagnostic]) -> Void - ) throws { + ) async throws { let dependencies = try deps.map { try $0.convert(baseURL: packagesDir, identityResolver: self.identityResolver) } let observability = ObservabilitySystem.makeForTesting() - observability.topScope.trap { + await observability.topScope.asyncTrap { let rootInput = PackageGraphRootInput( packages: try rootPaths(for: roots), dependencies: dependencies ) let workspace = try self.getOrCreateWorkspace() - try workspace.updateDependencies(root: rootInput, packages: packages, observabilityScope: observability.topScope) + try await workspace.updateDependencies(root: rootInput, packages: packages, observabilityScope: observability.topScope) } result(observability.diagnostics) } @@ -461,7 +466,7 @@ public final class MockWorkspace { roots: [String] = [], deps: [MockDependency] = [], _ result: ([(PackageReference, Workspace.PackageStateChange)]?, [Basics.Diagnostic]) -> Void - ) throws { + ) async throws { let dependencies = try deps.map { try $0.convert(baseURL: packagesDir, identityResolver: self.identityResolver) } let rootInput = PackageGraphRootInput( packages: try rootPaths(for: roots), @@ -469,9 +474,9 @@ public final class MockWorkspace { ) let observability = ObservabilitySystem.makeForTesting() - let changes = observability.topScope.trap { () -> [(PackageReference, Workspace.PackageStateChange)]? in + let changes = await observability.topScope.asyncTrap { () async throws -> [(PackageReference, Workspace.PackageStateChange)]? in let workspace = try self.getOrCreateWorkspace() - return try workspace.updateDependencies(root: rootInput, dryRun: true, observabilityScope: observability.topScope) + return try await workspace.updateDependencies(root: rootInput, dryRun: true, observabilityScope: observability.topScope) } ?? nil result(changes, observability.diagnostics) } @@ -480,9 +485,9 @@ public final class MockWorkspace { roots: [String] = [], deps: [MockDependency], _ result: (ModulesGraph, [Basics.Diagnostic]) -> Void - ) throws { + ) async throws { let dependencies = try deps.map { try $0.convert(baseURL: packagesDir, identityResolver: self.identityResolver) } - try self.checkPackageGraph(roots: roots, dependencies: dependencies, result) + try await self.checkPackageGraph(roots: roots, dependencies: dependencies, result) } public func checkPackageGraph( @@ -491,14 +496,14 @@ public final class MockWorkspace { forceResolvedVersions: Bool = false, expectedSigningEntities: [PackageIdentity: RegistryReleaseMetadata.SigningEntity] = [:], _ result: (ModulesGraph, [Basics.Diagnostic]) throws -> Void - ) throws { + ) async throws { let observability = ObservabilitySystem.makeForTesting() let rootInput = PackageGraphRootInput( packages: try rootPaths(for: roots), dependencies: dependencies ) let workspace = try self.getOrCreateWorkspace() do { - let graph = try workspace.loadPackageGraph( + let graph = try await workspace.loadPackageGraph( rootInput: rootInput, forceResolvedVersions: forceResolvedVersions, expectedSigningEntities: expectedSigningEntities, @@ -518,9 +523,9 @@ public final class MockWorkspace { roots: [String] = [], deps: [MockDependency], _ result: ([Basics.Diagnostic]) -> Void - ) throws { + ) async throws { let dependencies = try deps.map { try $0.convert(baseURL: packagesDir, identityResolver: self.identityResolver) } - self.checkPackageGraphFailure(roots: roots, dependencies: dependencies, result) + await self.checkPackageGraphFailure(roots: roots, dependencies: dependencies, result) } public func checkPackageGraphFailure( @@ -528,15 +533,15 @@ public final class MockWorkspace { dependencies: [PackageDependency] = [], forceResolvedVersions: Bool = false, _ result: ([Basics.Diagnostic]) -> Void - ) { + ) async { let observability = ObservabilitySystem.makeForTesting() - observability.topScope.trap { + await observability.topScope.asyncTrap { let rootInput = PackageGraphRootInput( packages: try rootPaths(for: roots), dependencies: dependencies ) let workspace = try self.getOrCreateWorkspace() - try workspace.loadPackageGraph( + try await workspace.loadPackageGraph( rootInput: rootInput, forceResolvedVersions: forceResolvedVersions, observabilityScope: observability.topScope @@ -556,13 +561,13 @@ public final class MockWorkspace { let pinsStore = try workspace.pinsStore.load() let rootInput = PackageGraphRootInput(packages: try rootPaths(for: roots.map { $0.name }), dependencies: []) - let rootManifests = try await workspace.loadRootManifests( + let rootManifests = await workspace.loadRootManifests( packages: rootInput.packages, observabilityScope: observability.topScope ) let root = PackageGraphRoot(input: rootInput, manifests: rootManifests, observabilityScope: observability.topScope) - let dependencyManifests = try workspace.loadDependencyManifests( + let dependencyManifests = try await workspace.loadDependencyManifests( root: root, observabilityScope: observability.topScope ) @@ -771,16 +776,16 @@ public final class MockWorkspace { roots: [String] = [], deps: [MockDependency] = [], _ result: (Workspace.DependencyManifests, [Basics.Diagnostic]) -> Void - ) throws { + ) async throws { let observability = ObservabilitySystem.makeForTesting() let dependencies = try deps.map { try $0.convert(baseURL: packagesDir, identityResolver: self.identityResolver) } let workspace = try self.getOrCreateWorkspace() let rootInput = PackageGraphRootInput( packages: try rootPaths(for: roots), dependencies: dependencies ) - let rootManifests = try temp_await { workspace.loadRootManifests(packages: rootInput.packages, observabilityScope: observability.topScope, completion: $0) } + let rootManifests = await workspace.loadRootManifests(packages: rootInput.packages, observabilityScope: observability.topScope) let graphRoot = PackageGraphRoot(input: rootInput, manifests: rootManifests, observabilityScope: observability.topScope) - let manifests = try workspace.loadDependencyManifests( + let manifests = try await workspace.loadDependencyManifests( root: graphRoot, observabilityScope: observability.topScope )