From e50785461e0e73e7c8811d6fb150142f1cac6459 Mon Sep 17 00:00:00 2001 From: fortmarek Date: Tue, 14 Jan 2025 23:16:31 +0100 Subject: [PATCH 1/4] Make WorkspaceState into actor to make it thread-safe --- .../PackageCommands/ResetCommands.swift | 4 +- Sources/CoreCommands/BuildSystemSupport.swift | 2 +- Sources/CoreCommands/SwiftCommandState.swift | 10 +- .../Workspace/Workspace+BinaryArtifacts.swift | 16 +- .../Workspace/Workspace+Dependencies.swift | 35 +- Sources/Workspace/Workspace+Editing.swift | 10 +- Sources/Workspace/Workspace+Manifests.swift | 32 +- Sources/Workspace/Workspace+Prebuilts.swift | 4 +- Sources/Workspace/Workspace+Registry.swift | 4 +- .../Workspace+ResolvedPackages.swift | 6 +- .../Workspace/Workspace+SourceControl.swift | 6 +- Sources/Workspace/Workspace+State.swift | 2 +- Sources/Workspace/Workspace.swift | 34 +- .../_InternalTestSupport/MockWorkspace.swift | 34 +- .../WorkspaceTests/WorkspaceStateTests.swift | 56 +-- Tests/WorkspaceTests/WorkspaceTests.swift | 410 +++++++++--------- 16 files changed, 334 insertions(+), 331 deletions(-) diff --git a/Sources/Commands/PackageCommands/ResetCommands.swift b/Sources/Commands/PackageCommands/ResetCommands.swift index fd2a872c228..5981d3f85ef 100644 --- a/Sources/Commands/PackageCommands/ResetCommands.swift +++ b/Sources/Commands/PackageCommands/ResetCommands.swift @@ -45,8 +45,8 @@ extension SwiftPackageCommand { @OptionGroup(visibility: .hidden) var globalOptions: GlobalOptions - func run(_ swiftCommandState: SwiftCommandState) throws { - try swiftCommandState.getActiveWorkspace().reset(observabilityScope: swiftCommandState.observabilityScope) + func run(_ swiftCommandState: SwiftCommandState) async throws { + try await swiftCommandState.getActiveWorkspace().reset(observabilityScope: swiftCommandState.observabilityScope) } } } diff --git a/Sources/CoreCommands/BuildSystemSupport.swift b/Sources/CoreCommands/BuildSystemSupport.swift index c9b3d7b681d..68473683ae3 100644 --- a/Sources/CoreCommands/BuildSystemSupport.swift +++ b/Sources/CoreCommands/BuildSystemSupport.swift @@ -40,7 +40,7 @@ private struct NativeBuildSystemFactory: BuildSystemFactory { return try BuildOperation( productsBuildParameters: try productsBuildParameters ?? self.swiftCommandState.productsBuildParameters, toolsBuildParameters: try toolsBuildParameters ?? self.swiftCommandState.toolsBuildParameters, - cacheBuildManifest: cacheBuildManifest && self.swiftCommandState.canUseCachedBuildManifest(), + cacheBuildManifest: await self.swiftCommandState.canUseCachedBuildManifest() && cacheBuildManifest, packageGraphLoader: packageGraphLoader ?? { try await self.swiftCommandState.loadPackageGraph( explicitProduct: explicitProduct, diff --git a/Sources/CoreCommands/SwiftCommandState.swift b/Sources/CoreCommands/SwiftCommandState.swift index b64d2736068..b2f85792d20 100644 --- a/Sources/CoreCommands/SwiftCommandState.swift +++ b/Sources/CoreCommands/SwiftCommandState.swift @@ -99,13 +99,13 @@ extension _SwiftCommand { } public protocol SwiftCommand: ParsableCommand, _SwiftCommand { - func run(_ swiftCommandState: SwiftCommandState) throws + func run(_ swiftCommandState: SwiftCommandState) async throws } extension SwiftCommand { public static var _errorLabel: String { "error" } - public func run() throws { + public func run() async throws { let swiftCommandState = try SwiftCommandState( options: globalOptions, toolWorkspaceConfiguration: self.toolWorkspaceConfiguration, @@ -119,7 +119,7 @@ extension SwiftCommand { swiftCommandState.buildSystemProvider = try buildSystemProvider(swiftCommandState) var toolError: Error? = .none do { - try self.run(swiftCommandState) + try await self.run(swiftCommandState) if swiftCommandState.observabilityScope.errorsReported || swiftCommandState.executionStatus == .failure { throw ExitCode.failure } @@ -689,7 +689,7 @@ public final class SwiftCommandState { try _manifestLoader.get() } - public func canUseCachedBuildManifest() throws -> Bool { + public func canUseCachedBuildManifest() async throws -> Bool { if !self.options.caching.cacheBuildManifest { return false } @@ -706,7 +706,7 @@ public final class SwiftCommandState { // Perform steps for build manifest caching if we can enabled it. // // FIXME: We don't add edited packages in the package structure command yet (SR-11254). - let hasEditedPackages = try self.getActiveWorkspace().state.dependencies.contains(where: \.isEdited) + let hasEditedPackages = try await self.getActiveWorkspace().state.dependencies.contains(where: \.isEdited) if hasEditedPackages { return false } diff --git a/Sources/Workspace/Workspace+BinaryArtifacts.swift b/Sources/Workspace/Workspace+BinaryArtifacts.swift index 2efcf00bb08..3fb72f3b984 100644 --- a/Sources/Workspace/Workspace+BinaryArtifacts.swift +++ b/Sources/Workspace/Workspace+BinaryArtifacts.swift @@ -811,7 +811,7 @@ extension Workspace { var artifactsToDownload: [BinaryArtifactsManager.RemoteArtifact] = [] var artifactsToExtract: [ManagedArtifact] = [] - for artifact in state.artifacts { + for artifact in await state.artifacts { if !manifestArtifacts.local .contains(where: { $0.packageRef == artifact.packageRef && $0.targetName == artifact.targetName }) && !manifestArtifacts.remote @@ -822,7 +822,7 @@ extension Workspace { } for artifact in manifestArtifacts.local { - let existingArtifact = self.state.artifacts[ + let existingArtifact = await self.state.artifacts[ packageIdentity: artifact.packageRef.identity, targetName: artifact.targetName ] @@ -859,7 +859,7 @@ extension Workspace { } for artifact in manifestArtifacts.remote { - let existingArtifact = self.state.artifacts[ + let existingArtifact = await self.state.artifacts[ packageIdentity: artifact.packageRef.identity, targetName: artifact.targetName ] @@ -891,9 +891,9 @@ extension Workspace { } // Remove the artifacts and directories which are not needed anymore. - observabilityScope.trap { + await observabilityScope.trap { for artifact in artifactsToRemove { - state.artifacts.remove(packageIdentity: artifact.packageRef.identity, targetName: artifact.targetName) + await state.artifacts.remove(packageIdentity: artifact.packageRef.identity, targetName: artifact.targetName) if isAtArtifactsDirectory(artifact) { try fileSystem.removeFileTree(artifact.path) @@ -930,15 +930,15 @@ extension Workspace { // Add the new artifacts for artifact in artifactsToAdd { - self.state.artifacts.add(artifact) + await self.state.artifacts.add(artifact) } guard !observabilityScope.errorsReported else { throw Diagnostics.fatalError } - observabilityScope.trap { - try self.state.save() + await observabilityScope.trap { + try await self.state.save() } func isAtArtifactsDirectory(_ artifact: ManagedArtifact) -> Bool { diff --git a/Sources/Workspace/Workspace+Dependencies.swift b/Sources/Workspace/Workspace+Dependencies.swift index 2e2480e5ba1..2469073898c 100644 --- a/Sources/Workspace/Workspace+Dependencies.swift +++ b/Sources/Workspace/Workspace+Dependencies.swift @@ -169,7 +169,7 @@ extension Workspace { } // Update the resolved file. - try self.saveResolvedFile( + try await self.saveResolvedFile( resolvedPackagesStore: resolvedPackagesStore, dependencyManifests: updatedDependencyManifests, originHash: resolvedFileOriginHash, @@ -219,7 +219,7 @@ extension Workspace { case .update(let forceResolution): return try await resolveAndUpdateResolvedFile(forceResolution: forceResolution) case .bestEffort: - guard !self.state.dependencies.hasEditedDependencies() else { + guard await !self.state.dependencies.hasEditedDependencies() else { return try await resolveAndUpdateResolvedFile(forceResolution: false) } guard self.fileSystem.exists(self.location.resolvedVersionsFile) else { @@ -410,9 +410,10 @@ extension Workspace { // // We require cloning if there is no checkout or if the checkout doesn't // match with the pin. + let dependencies = await state.dependencies let requiredResolvedPackages = resolvedPackagesStore.resolvedPackages.values.filter { pin in // also compare the location in case it has changed - guard let dependency = state.dependencies[comparingLocation: pin.packageRef] else { + guard let dependency = dependencies[comparingLocation: pin.packageRef] else { return true } switch dependency.state { @@ -528,7 +529,7 @@ extension Workspace { } // load and update the `Package.resolved` store with any changes from loading the top level dependencies - guard let resolvedPackagesStore = self.loadAndUpdateResolvedPackagesStore( + guard let resolvedPackagesStore = await self.loadAndUpdateResolvedPackagesStore( dependencyManifests: currentManifests, rootManifestsMinimumToolsVersion: rootManifestsMinimumToolsVersion, observabilityScope: observabilityScope @@ -559,7 +560,7 @@ extension Workspace { case .notRequired: // since nothing changed we can exit early, // but need update resolved file and download an missing binary artifact - try self.saveResolvedFile( + try await self.saveResolvedFile( resolvedPackagesStore: resolvedPackagesStore, dependencyManifests: currentManifests, originHash: resolvedFileOriginHash, @@ -630,7 +631,7 @@ extension Workspace { } // Update the resolved file. - try self.saveResolvedFile( + try await self.saveResolvedFile( resolvedPackagesStore: resolvedPackagesStore, dependencyManifests: updatedDependencyManifests, originHash: resolvedFileOriginHash, @@ -684,7 +685,7 @@ extension Workspace { // First remove the checkouts that are no longer required. for (packageRef, state) in packageStateChanges { - observabilityScope.makeChildScope( + await observabilityScope.makeChildScope( description: "removing unneeded checkouts", metadata: packageRef.diagnosticsMetadata ).trap { @@ -692,7 +693,7 @@ extension Workspace { case .added, .updated, .unchanged: break case .removed: - try self.remove(package: packageRef) + try await self.remove(package: packageRef) } } } @@ -777,8 +778,8 @@ extension Workspace { state: .custom(version: version, path: path), subpath: RelativePath(validating: "") ) - self.state.dependencies.add(dependency) - try self.state.save() + await self.state.dependencies.add(dependency) + try await self.state.save() return path } else { throw InternalError("invalid container for \(package.identity) of type \(package.kind)") @@ -805,8 +806,8 @@ extension Workspace { throw InternalError("invalid package type: \(package.kind)") } - self.state.dependencies.add(dependency) - try self.state.save() + await self.state.dependencies.add(dependency) + try await self.state.save() return path } } @@ -894,7 +895,7 @@ extension Workspace { dependencyManifests: DependencyManifests, rootManifestsMinimumToolsVersion: ToolsVersion, observabilityScope: ObservabilityScope - ) -> ResolvedPackagesStore? { + ) async -> ResolvedPackagesStore? { guard let resolvedPackagesStore = observabilityScope.trap({ try self.resolvedPackagesStore.load() }) else { return nil } @@ -904,7 +905,7 @@ extension Workspace { else { return nil } - for dependency in self.state.dependencies.filter(\.packageRef.kind.isResolvable) { + for dependency in await self.state.dependencies.filter(\.packageRef.kind.isResolvable) { // a required dependency that is already loaded (managed) should be represented in the `Package.resolved` store. // also comparing location as it may have changed at this point if requiredDependencies.contains(where: { $0.equalsIncludingLocation(dependency.packageRef) }) { @@ -1016,13 +1017,13 @@ extension Workspace { // Get the existing managed dependency for this package ref, if any. // first find by identity only since edit location may be different by design - var currentDependency = self.state.dependencies[binding.package.identity] + var currentDependency = await self.state.dependencies[binding.package.identity] // Check if this is an edited dependency. if case .edited(let basedOn, _) = currentDependency?.state, let originalReference = basedOn?.packageRef { packageStateChanges[originalReference.identity] = (originalReference, .unchanged) } else { // if not edited, also compare by location since it may have changed - currentDependency = self.state.dependencies[comparingLocation: binding.package] + currentDependency = await self.state.dependencies[comparingLocation: binding.package] } switch binding.boundVersion { @@ -1124,7 +1125,7 @@ extension Workspace { } } // Set the state of any old package that might have been removed. - for packageRef in self.state.dependencies.lazy.map(\.packageRef) + for packageRef in await self.state.dependencies.lazy.map(\.packageRef) where packageStateChanges[packageRef.identity] == nil { packageStateChanges[packageRef.identity] = (packageRef, .removed) diff --git a/Sources/Workspace/Workspace+Editing.swift b/Sources/Workspace/Workspace+Editing.swift index 966db0108eb..6e3cdda2a48 100644 --- a/Sources/Workspace/Workspace+Editing.swift +++ b/Sources/Workspace/Workspace+Editing.swift @@ -28,7 +28,7 @@ extension Workspace { observabilityScope: ObservabilityScope ) async throws { // Look up the dependency and check if we can edit it. - guard let dependency = self.state.dependencies[.plain(packageIdentity)] else { + guard let dependency = await self.state.dependencies[.plain(packageIdentity)] else { observabilityScope.emit(.dependencyNotFound(packageName: packageIdentity)) return } @@ -157,10 +157,10 @@ extension Workspace { } // Save the new state. - try self.state.dependencies.add( + try await self.state.dependencies.add( dependency.edited(subpath: RelativePath(validating: packageIdentity), unmanagedPath: path) ) - try self.state.save() + try await self.state.save() } /// Unedit a managed dependency. See public API unedit(packageName:forceRemove:). @@ -222,8 +222,8 @@ extension Workspace { ) } else { // The original dependency was removed, update the managed dependency state. - self.state.dependencies.remove(dependency.packageRef.identity) - try self.state.save() + await self.state.dependencies.remove(dependency.packageRef.identity) + try await self.state.save() } // Resolve the dependencies if workspace root is provided. We do this to diff --git a/Sources/Workspace/Workspace+Manifests.swift b/Sources/Workspace/Workspace+Manifests.swift index 1e80ccc4eb3..637c0b30bc7 100644 --- a/Sources/Workspace/Workspace+Manifests.swift +++ b/Sources/Workspace/Workspace+Manifests.swift @@ -412,21 +412,21 @@ extension Workspace { automaticallyAddManagedDependencies: Bool = false, observabilityScope: ObservabilityScope ) async throws -> DependencyManifests { - let prepopulateManagedDependencies: ([PackageReference]) throws -> Void = { refs in + let prepopulateManagedDependencies: ([PackageReference]) async throws -> Void = { refs in // pre-populate managed dependencies if we are asked to do so (this happens when resolving to a resolved // file) if automaticallyAddManagedDependencies { - try refs.forEach { ref in + for ref in refs { // Since we are creating managed dependencies based on the resolved file in this mode, but local // packages aren't part of that file, they will be missing from it. So we're eagerly adding them // here, but explicitly don't add any that are overridden by a root with the same identity since // that would lead to loading the given package twice, once as a root and once as a dependency // which violates various assumptions. if case .fileSystem = ref.kind, !root.manifests.keys.contains(ref.identity) { - try self.state.dependencies.add(.fileSystem(packageRef: ref)) + try await self.state.dependencies.add(.fileSystem(packageRef: ref)) } } - observabilityScope.trap { try self.state.save() } + await observabilityScope.trap { try await self.state.save() } } } @@ -437,15 +437,15 @@ extension Workspace { } // Make a copy of dependencies as we might mutate them in the for loop. - let dependenciesToCheck = Array(self.state.dependencies) + let dependenciesToCheck = await Array(self.state.dependencies) // Remove any managed dependency which has become a root. for dependency in dependenciesToCheck { if root.packages.keys.contains(dependency.packageRef.identity) { - observabilityScope.makeChildScope( + await observabilityScope.makeChildScope( description: "removing managed dependencies", metadata: dependency.packageRef.diagnosticsMetadata ).trap { - try self.remove(package: dependency.packageRef) + try await self.remove(package: dependency.packageRef) } } } @@ -466,7 +466,7 @@ extension Workspace { // Load root dependencies manifests (in parallel) let rootDependencies = root.dependencies.map(\.packageRef) - try prepopulateManagedDependencies(rootDependencies) + try await prepopulateManagedDependencies(rootDependencies) let rootDependenciesManifests = await self.loadManagedManifests( for: rootDependencies, observabilityScope: observabilityScope @@ -491,7 +491,7 @@ extension Workspace { let dependenciesRequired = pair.item.dependenciesRequired(for: pair.key.productFilter) let dependenciesToLoad = dependenciesRequired.map(\.packageRef) .filter { !loadedManifests.keys.contains($0.identity) } - try prepopulateManagedDependencies(dependenciesToLoad) + try await prepopulateManagedDependencies(dependenciesToLoad) let dependenciesManifests = await self.loadManagedManifests( for: dependenciesToLoad, observabilityScope: observabilityScope @@ -554,7 +554,7 @@ extension Workspace { var dependencies: [(Manifest, ManagedDependency, ProductFilter, FileSystem)] = [] for (identity, manifest, productFilter) in dependencyManifests { - guard let dependency = self.state.dependencies[identity] else { + guard let dependency = await self.state.dependencies[identity] else { throw InternalError("dependency not found for \(identity) at \(manifest.packageLocation)") } @@ -609,7 +609,7 @@ extension Workspace { // 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 { + guard let managedDependency = await self.state.dependencies[comparingLocation: package] else { return nil } @@ -767,12 +767,12 @@ extension Workspace { 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() + if await !self.state.dependencies.isEmpty, await !self.state.stateFileExists() { + try? await self.state.reset() } // Make a copy of dependencies as we might mutate them in the for loop. - let allDependencies = Array(self.state.dependencies) + let allDependencies = await Array(self.state.dependencies) for dependency in allDependencies { await observabilityScope.makeChildScope( description: "copying managed dependencies", @@ -841,8 +841,8 @@ extension Workspace { .emit(.editedDependencyMissing(packageName: dependency.packageRef.identity.description)) case .fileSystem: - self.state.dependencies.remove(dependency.packageRef.identity) - try self.state.save() + await self.state.dependencies.remove(dependency.packageRef.identity) + try await self.state.save() } } } diff --git a/Sources/Workspace/Workspace+Prebuilts.swift b/Sources/Workspace/Workspace+Prebuilts.swift index cf766930115..7b58f142e98 100644 --- a/Sources/Workspace/Workspace+Prebuilts.swift +++ b/Sources/Workspace/Workspace+Prebuilts.swift @@ -508,8 +508,8 @@ extension Workspace { products: library.products, cModules: library.cModules ) - self.state.prebuilts.add(managedPrebuilt) - try self.state.save() + await self.state.prebuilts.add(managedPrebuilt) + try await self.state.save() } } } diff --git a/Sources/Workspace/Workspace+Registry.swift b/Sources/Workspace/Workspace+Registry.swift index 0fc2a25c494..d81979c421b 100644 --- a/Sources/Workspace/Workspace+Registry.swift +++ b/Sources/Workspace/Workspace+Registry.swift @@ -424,14 +424,14 @@ extension Workspace { debug: "adding '\(package.identity)' (\(package.locationString)) to managed dependencies", metadata: package.diagnosticsMetadata ) - try self.state.dependencies.add( + try await self.state.dependencies.add( .registryDownload( packageRef: package, version: version, subpath: downloadPath.relative(to: self.location.registryDownloadDirectory) ) ) - try self.state.save() + try await self.state.save() return downloadPath } diff --git a/Sources/Workspace/Workspace+ResolvedPackages.swift b/Sources/Workspace/Workspace+ResolvedPackages.swift index a81fd2aed53..4be96b417a1 100644 --- a/Sources/Workspace/Workspace+ResolvedPackages.swift +++ b/Sources/Workspace/Workspace+ResolvedPackages.swift @@ -24,13 +24,13 @@ extension Workspace { originHash: String, rootManifestsMinimumToolsVersion: ToolsVersion, observabilityScope: ObservabilityScope - ) throws { + ) async throws { var dependenciesToSaveAsResolved = [ManagedDependency]() let requiredDependencies = try dependencyManifests.requiredPackages.filter(\.kind.isResolvable) for dependency in requiredDependencies { - if let managedDependency = self.state.dependencies[comparingLocation: dependency] { + if let managedDependency = await self.state.dependencies[comparingLocation: dependency] { dependenciesToSaveAsResolved.append(managedDependency) - } else if let managedDependency = self.state.dependencies[dependency.identity] { + } else if let managedDependency = await self.state.dependencies[dependency.identity] { observabilityScope .emit( info: "required dependency '\(dependency.identity)' from '\(dependency.locationString)' was not found in managed dependencies, using alternative location '\(managedDependency.packageRef.locationString)' instead" diff --git a/Sources/Workspace/Workspace+SourceControl.swift b/Sources/Workspace/Workspace+SourceControl.swift index e963fab064b..bd101b8c005 100644 --- a/Sources/Workspace/Workspace+SourceControl.swift +++ b/Sources/Workspace/Workspace+SourceControl.swift @@ -69,14 +69,14 @@ extension Workspace { debug: "adding '\(package.identity)' (\(package.locationString)) to managed dependencies", metadata: package.diagnosticsMetadata ) - try self.state.dependencies.add( + try await self.state.dependencies.add( .sourceControlCheckout( packageRef: package, state: checkoutState, subpath: checkoutPath.relative(to: self.location.repositoriesCheckoutsDirectory) ) ) - try self.state.save() + try await self.state.save() // Inform the delegate that we're done. let duration = start.distance(to: .now()) @@ -138,7 +138,7 @@ extension Workspace { // If we already have it, fetch to update the repo from its remote. // also compare the location as it may have changed - if let dependency = self.state.dependencies[comparingLocation: package] { + if let dependency = await self.state.dependencies[comparingLocation: package] { let checkoutPath = self.location.repositoriesCheckoutSubdirectory(for: dependency) // Make sure the directory is not missing (we will have to clone again if not). diff --git a/Sources/Workspace/Workspace+State.swift b/Sources/Workspace/Workspace+State.swift index 533cac4adf2..46f26925b43 100644 --- a/Sources/Workspace/Workspace+State.swift +++ b/Sources/Workspace/Workspace+State.swift @@ -19,7 +19,7 @@ import SourceControl import struct TSCUtility.Version /// Represents the workspace internal state persisted on disk. -public final class WorkspaceState { +public actor WorkspaceState { /// The dependencies managed by the Workspace. public private(set) var dependencies: Workspace.ManagedDependencies diff --git a/Sources/Workspace/Workspace.swift b/Sources/Workspace/Workspace.swift index 6199e356e5e..8e0807b2c09 100644 --- a/Sources/Workspace/Workspace.swift +++ b/Sources/Workspace/Workspace.swift @@ -663,7 +663,7 @@ extension Workspace { root: PackageGraphRootInput, observabilityScope: ObservabilityScope ) async throws { - guard let dependency = self.state.dependencies[.plain(packageIdentity)] else { + guard let dependency = await self.state.dependencies[.plain(packageIdentity)] else { observabilityScope.emit(.dependencyNotFound(packageName: packageIdentity)) return } @@ -725,7 +725,7 @@ extension Workspace { observabilityScope: ObservabilityScope ) async throws { // Look up the dependency and check if we can pin it. - guard let dependency = self.state.dependencies[.plain(packageName)] else { + guard let dependency = await self.state.dependencies[.plain(packageName)] else { throw StringError("dependency '\(packageName)' was not found") } @@ -841,15 +841,15 @@ extension Workspace { /// /// - Parameters: /// - observabilityScope: The observability scope that reports errors, warnings, etc - public func reset(observabilityScope: ObservabilityScope) { - let removed = observabilityScope.trap { () -> Bool in + public func reset(observabilityScope: ObservabilityScope) async { + let removed = await observabilityScope.trap { () -> Bool in try self.fileSystem.chmod( .userWritable, path: self.location.repositoriesCheckoutsDirectory, options: [.recursive, .onlyFiles] ) // Reset state. - try self.resetState() + try await self.resetState() return true } @@ -871,8 +871,8 @@ extension Workspace { } // FIXME: @testable internal - public func resetState() throws { - try self.state.reset() + public func resetState() async throws { + try await self.state.reset() } /// Cancel the active dependency resolution operation. @@ -942,7 +942,7 @@ extension Workspace { // long running host processes (ie IDEs) need this in case other SwiftPM processes (ie CLI) made changes to the // state // such hosts processes call loadPackageGraph to make sure the workspace state is correct - try self.state.reload() + try await self.state.reload() // Perform dependency resolution, if required. let manifests = try await self._resolve( @@ -952,7 +952,7 @@ extension Workspace { observabilityScope: observabilityScope ) - let binaryArtifacts = self.state.artifacts + let binaryArtifacts = await self.state.artifacts .reduce(into: [PackageIdentity: [String: BinaryArtifact]]()) { partial, artifact in partial[artifact.packageRef.identity, default: [:]][artifact.targetName] = BinaryArtifact( kind: artifact.kind, @@ -961,7 +961,7 @@ extension Workspace { ) } - let prebuilts: [PackageIdentity: [String: PrebuiltLibrary]] = self.state.prebuilts.reduce(into: .init()) { + let prebuilts: [PackageIdentity: [String: PrebuiltLibrary]] = await self.state.prebuilts.reduce(into: .init()) { let prebuilt = PrebuiltLibrary(packageRef: $1.packageRef, libraryName: $1.libraryName, path: $1.path, products: $1.products, cModules: $1.cModules) for product in $1.products { $0[$1.packageRef.identity, default: [:]][product] = prebuilt @@ -1292,8 +1292,8 @@ extension Workspace { /// /// - Parameters: /// - package: The package to remove - func remove(package: PackageReference) throws { - guard let dependency = self.state.dependencies[package.identity] else { + func remove(package: PackageReference) async throws { + guard let dependency = await self.state.dependencies[package.identity] else { throw InternalError("trying to remove \(package.identity) which isn't in workspace") } @@ -1302,8 +1302,8 @@ extension Workspace { // // Note that we don't actually remove a local package from disk. if case .fileSystem = dependency.state { - self.state.dependencies.remove(package.identity) - try self.state.save() + await self.state.dependencies.remove(package.identity) + try await self.state.save() return } @@ -1323,10 +1323,10 @@ extension Workspace { basedOn: .none, unmanagedPath: unmanagedPath ) - self.state.dependencies.add(updatedDependency) + await self.state.dependencies.add(updatedDependency) } else { dependencyToRemove = dependency - self.state.dependencies.remove(dependencyToRemove.packageRef.identity) + await self.state.dependencies.remove(dependencyToRemove.packageRef.identity) } switch package.kind { @@ -1341,7 +1341,7 @@ extension Workspace { } // Save the state. - try self.state.save() + try await self.state.save() } } diff --git a/Sources/_InternalTestSupport/MockWorkspace.swift b/Sources/_InternalTestSupport/MockWorkspace.swift index e097a85dbe8..b32246b3739 100644 --- a/Sources/_InternalTestSupport/MockWorkspace.swift +++ b/Sources/_InternalTestSupport/MockWorkspace.swift @@ -379,9 +379,9 @@ public final class MockWorkspace { private var _workspace: Workspace? - public func closeWorkspace(resetState: Bool = true, resetResolvedFile: Bool = true) throws { + public func closeWorkspace(resetState: Bool = true, resetResolvedFile: Bool = true) async throws { if resetState { - try self._workspace?.resetState() + try await self._workspace?.resetState() } if resetResolvedFile { try self._workspace.map { @@ -455,11 +455,11 @@ public final class MockWorkspace { result(observability.diagnostics) } - public func checkReset(_ result: ([Basics.Diagnostic]) -> Void) { + public func checkReset(_ result: ([Basics.Diagnostic]) -> Void) async { let observability = ObservabilitySystem.makeForTesting() - observability.topScope.trap { + await observability.topScope.trap { let workspace = try self.getOrCreateWorkspace() - workspace.reset(observabilityScope: observability.topScope) + await workspace.reset(observabilityScope: observability.topScope) } result(observability.diagnostics) } @@ -609,7 +609,7 @@ public final class MockWorkspace { resolvedPackages: [PackageReference: CheckoutState] = [:], managedDependencies: [AbsolutePath: Workspace.ManagedDependency] = [:], managedArtifacts: [Workspace.ManagedArtifact] = [] - ) throws { + ) async throws { let resolvedPackages = resolvedPackages.mapValues { checkoutState -> ResolvedPackagesStore.ResolutionState in switch checkoutState { case .version(let version, let revision): @@ -620,14 +620,14 @@ public final class MockWorkspace { return .revision(revision.identifier) } } - try self.set(resolvedPackages: resolvedPackages, managedDependencies: managedDependencies, managedArtifacts: managedArtifacts) + try await self.set(resolvedPackages: resolvedPackages, managedDependencies: managedDependencies, managedArtifacts: managedArtifacts) } public func set( resolvedPackages: [PackageReference: ResolvedPackagesStore.ResolutionState], managedDependencies: [AbsolutePath: Workspace.ManagedDependency] = [:], managedArtifacts: [Workspace.ManagedArtifact] = [] - ) throws { + ) async throws { let workspace = try self.getOrCreateWorkspace() let resolvedPackagesStore = try workspace.resolvedPackagesStore.load() @@ -644,21 +644,21 @@ public final class MockWorkspace { } else { try self.fileSystem.createDirectory(managedPath, recursive: true) } - workspace.state.dependencies.add(dependency.value) + await workspace.state.dependencies.add(dependency.value) } for artifact in managedArtifacts { // create an empty directory representing the artifact try self.fileSystem.createDirectory(artifact.path, recursive: true) - workspace.state.artifacts.add(artifact) + await workspace.state.artifacts.add(artifact) } - try workspace.state.save() + try await workspace.state.save() } - public func resetState() throws { + public func resetState() async throws { let workspace = try self.getOrCreateWorkspace() - try workspace.resetState() + try await workspace.resetState() } public enum State { @@ -811,19 +811,19 @@ public final class MockWorkspace { result(manifests, observability.diagnostics) } - public func checkManagedDependencies(file: StaticString = #file, line: UInt = #line, _ result: (ManagedDependencyResult) throws -> Void) { + public func checkManagedDependencies(file: StaticString = #file, line: UInt = #line, _ result: (ManagedDependencyResult) throws -> Void) async { do { let workspace = try self.getOrCreateWorkspace() - try result(ManagedDependencyResult(workspace.state.dependencies)) + try await result(ManagedDependencyResult(workspace.state.dependencies)) } catch { XCTFail("Failed with error \(error.interpolationDescription)", file: file, line: line) } } - public func checkManagedArtifacts(file: StaticString = #file, line: UInt = #line, _ result: (ManagedArtifactResult) throws -> Void) { + public func checkManagedArtifacts(file: StaticString = #file, line: UInt = #line, _ result: (ManagedArtifactResult) throws -> Void) async { do { let workspace = try self.getOrCreateWorkspace() - try result(ManagedArtifactResult(workspace.state.artifacts)) + try await result(ManagedArtifactResult(workspace.state.artifacts)) } catch { XCTFail("Failed with error \(error.interpolationDescription)", file: file, line: line) } diff --git a/Tests/WorkspaceTests/WorkspaceStateTests.swift b/Tests/WorkspaceTests/WorkspaceStateTests.swift index bee32b97ed6..5346bbada06 100644 --- a/Tests/WorkspaceTests/WorkspaceStateTests.swift +++ b/Tests/WorkspaceTests/WorkspaceStateTests.swift @@ -15,7 +15,7 @@ import Basics import XCTest final class WorkspaceStateTests: XCTestCase { - func testV4Format() throws { + func testV4Format() async throws { let fs = InMemoryFileSystem() let buildDir = AbsolutePath("/.build") @@ -82,13 +82,13 @@ final class WorkspaceStateTests: XCTestCase { """ ) - let state = WorkspaceState(fileSystem: fs, storageDirectory: buildDir) - XCTAssertTrue(state.dependencies.contains(where: { $0.packageRef.identity == .plain("yams") })) - XCTAssertTrue(state.dependencies.contains(where: { $0.packageRef.identity == .plain("swift-tools-support-core") })) - XCTAssertTrue(state.dependencies.contains(where: { $0.packageRef.identity == .plain("swift-argument-parser") })) + let dependencies = await WorkspaceState(fileSystem: fs, storageDirectory: buildDir).dependencies + XCTAssertTrue(dependencies.contains(where: { $0.packageRef.identity == .plain("yams") })) + XCTAssertTrue(dependencies.contains(where: { $0.packageRef.identity == .plain("swift-tools-support-core") })) + XCTAssertTrue(dependencies.contains(where: { $0.packageRef.identity == .plain("swift-argument-parser") })) } - func testV4FormatWithPath() throws { + func testV4FormatWithPath() async throws { let fs = InMemoryFileSystem() let buildDir = AbsolutePath("/.build") @@ -155,13 +155,13 @@ final class WorkspaceStateTests: XCTestCase { """ ) - let state = WorkspaceState(fileSystem: fs, storageDirectory: buildDir) - XCTAssertTrue(state.dependencies.contains(where: { $0.packageRef.identity == .plain("yams") })) - XCTAssertTrue(state.dependencies.contains(where: { $0.packageRef.identity == .plain("swift-tools-support-core") })) - XCTAssertTrue(state.dependencies.contains(where: { $0.packageRef.identity == .plain("swift-argument-parser") })) + let dependencies = await WorkspaceState(fileSystem: fs, storageDirectory: buildDir).dependencies + XCTAssertTrue(dependencies.contains(where: { $0.packageRef.identity == .plain("yams") })) + XCTAssertTrue(dependencies.contains(where: { $0.packageRef.identity == .plain("swift-tools-support-core") })) + XCTAssertTrue(dependencies.contains(where: { $0.packageRef.identity == .plain("swift-argument-parser") })) } - func testV5Format() throws { + func testV5Format() async throws { let fs = InMemoryFileSystem() let buildDir = AbsolutePath("/.build") @@ -228,13 +228,13 @@ final class WorkspaceStateTests: XCTestCase { """ ) - let state = WorkspaceState(fileSystem: fs, storageDirectory: buildDir) - XCTAssertTrue(state.dependencies.contains(where: { $0.packageRef.identity == .plain("yams") })) - XCTAssertTrue(state.dependencies.contains(where: { $0.packageRef.identity == .plain("swift-tools-support-core") })) - XCTAssertTrue(state.dependencies.contains(where: { $0.packageRef.identity == .plain("swift-argument-parser") })) + let dependencies = await WorkspaceState(fileSystem: fs, storageDirectory: buildDir).dependencies + XCTAssertTrue(dependencies.contains(where: { $0.packageRef.identity == .plain("yams") })) + XCTAssertTrue(dependencies.contains(where: { $0.packageRef.identity == .plain("swift-tools-support-core") })) + XCTAssertTrue(dependencies.contains(where: { $0.packageRef.identity == .plain("swift-argument-parser") })) } - func testSavedDependenciesAreSorted() throws { + func testSavedDependenciesAreSorted() async throws { let fs = InMemoryFileSystem() let buildDir = AbsolutePath("/.build") @@ -288,7 +288,7 @@ final class WorkspaceStateTests: XCTestCase { ) let state = WorkspaceState(fileSystem: fs, storageDirectory: buildDir) - try state.save() + try await state.save() let serialized: String = try fs.readFileContents(statePath) @@ -298,7 +298,7 @@ final class WorkspaceStateTests: XCTestCase { XCTAssertTrue(argpRange.lowerBound < yamsRange.lowerBound) } - func testArtifacts() throws { + func testArtifacts() async throws { let fs = InMemoryFileSystem() let buildDir = AbsolutePath("/.build") @@ -362,14 +362,14 @@ final class WorkspaceStateTests: XCTestCase { """ ) - let state = WorkspaceState(fileSystem: fs, storageDirectory: buildDir) - XCTAssertTrue(state.artifacts.contains(where: { $0.packageRef.identity == .plain("foo") && $0.targetName == "foo" })) - XCTAssertTrue(state.artifacts.contains(where: { $0.packageRef.identity == .plain("foo") && $0.targetName == "bar" })) - XCTAssertTrue(state.artifacts.contains(where: { $0.packageRef.identity == .plain("bar") && $0.targetName == "bar" })) + let artifacts = await WorkspaceState(fileSystem: fs, storageDirectory: buildDir).artifacts + XCTAssertTrue(artifacts.contains(where: { $0.packageRef.identity == .plain("foo") && $0.targetName == "foo" })) + XCTAssertTrue(artifacts.contains(where: { $0.packageRef.identity == .plain("foo") && $0.targetName == "bar" })) + XCTAssertTrue(artifacts.contains(where: { $0.packageRef.identity == .plain("bar") && $0.targetName == "bar" })) } // rdar://86857825 - func testDuplicateDependenciesDoNotCrash() throws { + func testDuplicateDependenciesDoNotCrash() async throws { let fs = InMemoryFileSystem() let buildDir = AbsolutePath("/.build") @@ -422,14 +422,14 @@ final class WorkspaceStateTests: XCTestCase { """ ) - let state = WorkspaceState(fileSystem: fs, storageDirectory: buildDir) + let dependencies = await WorkspaceState(fileSystem: fs, storageDirectory: buildDir).dependencies // empty since we have dups so we warn and fail the loading // TODO: test for diagnostics when we can get them from the WorkspaceState initializer - XCTAssertTrue(state.dependencies.isEmpty) + XCTAssertTrue(dependencies.isEmpty) } // rdar://86857825 - func testDuplicateArtifactsDoNotCrash() throws { + func testDuplicateArtifactsDoNotCrash() async throws { let fs = InMemoryFileSystem() let buildDir = AbsolutePath("/.build") @@ -478,10 +478,10 @@ final class WorkspaceStateTests: XCTestCase { """ ) - let state = WorkspaceState(fileSystem: fs, storageDirectory: buildDir) + let artifacts = await WorkspaceState(fileSystem: fs, storageDirectory: buildDir).artifacts // empty since we have dups so we warn and fail the loading // TODO: test for diagnostics when we can get them from the WorkspaceState initializer - XCTAssertTrue(state.artifacts.isEmpty) + XCTAssertTrue(artifacts.isEmpty) } } diff --git a/Tests/WorkspaceTests/WorkspaceTests.swift b/Tests/WorkspaceTests/WorkspaceTests.swift index 457deeb04a6..73558cf8fec 100644 --- a/Tests/WorkspaceTests/WorkspaceTests.swift +++ b/Tests/WorkspaceTests/WorkspaceTests.swift @@ -91,7 +91,7 @@ final class WorkspaceTests: XCTestCase { } XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "baz", at: .checkout(.version("1.0.0"))) result.check(dependency: "quix", at: .checkout(.version("1.2.0"))) } @@ -132,13 +132,13 @@ final class WorkspaceTests: XCTestCase { ) // Close and reopen workspace. - try workspace.closeWorkspace(resetState: false) - workspace.checkManagedDependencies { result in + try await workspace.closeWorkspace(resetState: false) + await workspace.checkManagedDependencies { result in result.check(dependency: "baz", at: .checkout(.version("1.0.0"))) result.check(dependency: "quix", at: .checkout(.version("1.2.0"))) } - let stateFile = try workspace.getOrCreateWorkspace().state.storagePath + let stateFile = try await workspace.getOrCreateWorkspace().state.storagePath // Remove state file and check we can get the state back automatically. try fs.removeFileTree(stateFile) @@ -147,9 +147,9 @@ final class WorkspaceTests: XCTestCase { XCTAssertTrue(fs.exists(stateFile), "workspace state file should exist") // Remove state file and check we get back to a clean state. - try fs.removeFileTree(workspace.getOrCreateWorkspace().state.storagePath) - try workspace.closeWorkspace() - workspace.checkManagedDependencies { result in + try await fs.removeFileTree(workspace.getOrCreateWorkspace().state.storagePath) + try await workspace.closeWorkspace() + await workspace.checkManagedDependencies { result in result.checkEmpty() } } @@ -371,7 +371,7 @@ final class WorkspaceTests: XCTestCase { } XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "baz", at: .checkout(.version("1.0.1"))) } } @@ -601,7 +601,7 @@ final class WorkspaceTests: XCTestCase { } XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(notPresent: "baz") } XCTAssertNoMatch(workspace.delegate.events, [.equal("fetching package: /tmp/ws/pkgs/Baz")]) @@ -662,7 +662,7 @@ final class WorkspaceTests: XCTestCase { } XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "baz", at: .checkout(.version("1.0.0"))) } XCTAssertMatch( @@ -682,7 +682,7 @@ final class WorkspaceTests: XCTestCase { } XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(notPresent: "baz") } XCTAssertNoMatch( @@ -752,7 +752,7 @@ final class WorkspaceTests: XCTestCase { } XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.0.0"))) result.check(dependency: "bar", at: .checkout(.version("1.0.0"))) } @@ -819,7 +819,7 @@ final class WorkspaceTests: XCTestCase { } XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "a", at: .checkout(.version("1.0.0"))) result.check(dependency: "aa", at: .checkout(.version("1.0.0"))) } @@ -840,7 +840,7 @@ final class WorkspaceTests: XCTestCase { } XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "a", at: .checkout(.version("1.0.1"))) result.check(dependency: "aa", at: .checkout(.version("2.0.0"))) } @@ -953,7 +953,7 @@ final class WorkspaceTests: XCTestCase { path: cPackagePath ) - try workspace.set( + try await workspace.set( resolvedPackages: [bRef: v1_5, cRef: v2], managedDependencies: [ bPackagePath: .sourceControlCheckout(packageRef: bRef, state: v1_5, subpath: bPath) @@ -1015,7 +1015,7 @@ final class WorkspaceTests: XCTestCase { path: cPackagePath ) - try workspace.set( + try await workspace.set( resolvedPackages: [bRef: v1], managedDependencies: [ bPackagePath: .sourceControlCheckout(packageRef: bRef, state: v1, subpath: bPath), @@ -1078,7 +1078,7 @@ final class WorkspaceTests: XCTestCase { path: cPackagePath ) - try workspace.set( + try await workspace.set( resolvedPackages: [bRef: v1_5, cRef: v1_5], managedDependencies: [ bPackagePath: .sourceControlCheckout(packageRef: bRef, state: v1_5, subpath: bPath), @@ -1130,7 +1130,7 @@ final class WorkspaceTests: XCTestCase { path: cPackagePath ) - try testWorkspace.set( + try await testWorkspace.set( resolvedPackages: [cRef: v1_5], managedDependencies: [ cPackagePath: .sourceControlCheckout(packageRef: cRef, state: v1_5, subpath: cPath), @@ -1196,7 +1196,7 @@ final class WorkspaceTests: XCTestCase { path: cPackagePath ) - try workspace.set( + try await workspace.set( resolvedPackages: [bRef: v1_5], managedDependencies: [ bPackagePath: .sourceControlCheckout(packageRef: bRef, state: v1_5, subpath: bPath), @@ -1263,7 +1263,7 @@ final class WorkspaceTests: XCTestCase { path: cPackagePath ) - try workspace.set( + try await workspace.set( resolvedPackages: [bRef: v1_5, cRef: v1_5], managedDependencies: [ bPackagePath: .sourceControlCheckout(packageRef: bRef, state: v1_5, subpath: bPath), @@ -1331,7 +1331,7 @@ final class WorkspaceTests: XCTestCase { path: cPackagePath ) - try workspace.set( + try await workspace.set( resolvedPackages: [bRef: v1_5, cRef: master], managedDependencies: [ bPackagePath: .sourceControlCheckout(packageRef: bRef, state: v1_5, subpath: bPath), @@ -1399,7 +1399,7 @@ final class WorkspaceTests: XCTestCase { path: cPackagePath ) - try workspace.set( + try await workspace.set( resolvedPackages: [bRef: v1_5, cRef: v1_5], managedDependencies: [ bPackagePath: .sourceControlCheckout(packageRef: bRef, state: v1_5, subpath: bPath), @@ -1469,7 +1469,7 @@ final class WorkspaceTests: XCTestCase { path: cPackagePath ) - try workspace.set( + try await workspace.set( resolvedPackages: [bRef: v1_5, cRef: v2], managedDependencies: [ bPackagePath: .sourceControlCheckout(packageRef: bRef, state: v1_5, subpath: bPath), @@ -1575,7 +1575,7 @@ final class WorkspaceTests: XCTestCase { } XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.0.0"))) result.check(dependency: "bar", at: .checkout(.version("1.0.0"))) } @@ -1591,7 +1591,7 @@ final class WorkspaceTests: XCTestCase { } XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.5.0"))) } XCTAssertMatch( @@ -1664,7 +1664,7 @@ final class WorkspaceTests: XCTestCase { } XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.0.0"))) } @@ -1697,7 +1697,7 @@ final class WorkspaceTests: XCTestCase { } XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.0.0"))) } } @@ -1770,7 +1770,7 @@ final class WorkspaceTests: XCTestCase { try await workspace.checkPackageGraph(roots: ["Root"], deps: deps) { _, diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.0.0"))) result.check(dependency: "bar", at: .checkout(.version("1.0.0"))) } @@ -1782,7 +1782,7 @@ final class WorkspaceTests: XCTestCase { try await workspace.checkUpdate(roots: ["Root"], packages: ["Bar"]) { diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.0.0"))) result.check(dependency: "bar", at: .checkout(.version("1.0.0"))) } @@ -1791,7 +1791,7 @@ final class WorkspaceTests: XCTestCase { try await workspace.checkUpdate(roots: ["Root"], packages: ["Foo"]) { diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.5.0"))) result.check(dependency: "bar", at: .checkout(.version("1.0.0"))) } @@ -1800,7 +1800,7 @@ final class WorkspaceTests: XCTestCase { try await workspace.checkUpdate(roots: ["Root"]) { diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.5.0"))) result.check(dependency: "bar", at: .checkout(.version("1.2.0"))) } @@ -1864,7 +1864,7 @@ final class WorkspaceTests: XCTestCase { XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.0.0"))) } @@ -1872,7 +1872,7 @@ final class WorkspaceTests: XCTestCase { try fs.writeFileContents(buildArtifact, bytes: "Hi") // Check reset. - workspace.checkReset { diagnostics in + await workspace.checkReset { diagnostics in // Only the build artifact should be removed. XCTAssertFalse(fs.exists(buildArtifact)) XCTAssertFalse(fs.exists(ws.location.repositoriesCheckoutsDirectory)) @@ -1880,7 +1880,7 @@ final class WorkspaceTests: XCTestCase { XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.checkEmpty() } } @@ -2091,7 +2091,7 @@ final class WorkspaceTests: XCTestCase { } XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.branch("develop"))) result.check(dependency: "bar", at: .checkout(.revision(barRevision))) } @@ -2138,7 +2138,7 @@ final class WorkspaceTests: XCTestCase { } XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.2.3"))) } workspace.checkResolved { result in @@ -2149,7 +2149,7 @@ final class WorkspaceTests: XCTestCase { await workspace.checkResolve(pkg: "Foo", roots: ["Root"], version: "1.0.0") { diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.0.0"))) } workspace.checkResolved { result in @@ -2162,7 +2162,7 @@ final class WorkspaceTests: XCTestCase { result.check(diagnostic: .contains("'foo' 1.3.0"), severity: .error) } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.0.0"))) } workspace.checkResolved { result in @@ -2394,7 +2394,7 @@ final class WorkspaceTests: XCTestCase { await workspace.checkEdit(packageIdentity: "foo") { diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .edited(nil)) } XCTAssertTrue(fs.exists(fooPath)) @@ -2411,7 +2411,7 @@ final class WorkspaceTests: XCTestCase { result.check(diagnostic: .equal("dependency 'foo' already in edit mode"), severity: .error) } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .edited(nil)) } @@ -2427,7 +2427,7 @@ final class WorkspaceTests: XCTestCase { await workspace.checkEdit(packageIdentity: "bar", path: barPath, checkoutBranch: "dev") { diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "bar", at: .edited(barPath)) } let barRepo = try workspace.repositoryProvider.openWorkingCopy(at: barPath) as! InMemoryGitRepository @@ -2548,7 +2548,7 @@ final class WorkspaceTests: XCTestCase { await workspace.checkEdit(packageIdentity: "Foo") { diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .edited(nil)) } XCTAssertTrue(fs.exists(fooPath)) @@ -2565,7 +2565,7 @@ final class WorkspaceTests: XCTestCase { ) } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.0.0"))) } } @@ -2607,7 +2607,7 @@ final class WorkspaceTests: XCTestCase { await workspace.checkEdit(packageIdentity: "Foo") { diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .edited(nil)) } @@ -2624,14 +2624,14 @@ final class WorkspaceTests: XCTestCase { } // There should still be an entry for `foo`, which we can unedit. - let editedDependency = ws.state.dependencies[.plain("foo")] + let editedDependency = await ws.state.dependencies[.plain("foo")] if case .edited(let basedOn, _) = editedDependency?.state { XCTAssertNil(basedOn) } else { XCTFail("expected edited dependency") } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .edited(nil)) } @@ -2639,7 +2639,7 @@ final class WorkspaceTests: XCTestCase { await workspace.checkUnedit(packageIdentity: "Foo", roots: []) { diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.checkEmpty() } } @@ -2699,7 +2699,7 @@ final class WorkspaceTests: XCTestCase { } XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.0.0"))) result.check(dependency: "bar", at: .checkout(.version("1.0.0"))) } @@ -2708,7 +2708,7 @@ final class WorkspaceTests: XCTestCase { await workspace.checkEdit(packageIdentity: "Bar") { diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.0.0"))) result.check(dependency: "bar", at: .edited(nil)) } @@ -2729,7 +2729,7 @@ final class WorkspaceTests: XCTestCase { await workspace.checkResolve(pkg: "Foo", roots: ["Root"], version: "1.2.0") { diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.2.0"))) result.check(dependency: "bar", at: .edited(nil)) } @@ -2742,7 +2742,7 @@ final class WorkspaceTests: XCTestCase { try await workspace.checkUpdate(roots: ["Root"]) { diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.3.2"))) result.check(dependency: "bar", at: .edited(nil)) } @@ -2755,7 +2755,7 @@ final class WorkspaceTests: XCTestCase { await workspace.checkUnedit(packageIdentity: "bar", roots: ["Root"]) { diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.3.2"))) result.check(dependency: "bar", at: .checkout(.version("1.0.0"))) } @@ -2829,7 +2829,7 @@ final class WorkspaceTests: XCTestCase { } XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.0.0"))) } @@ -2843,7 +2843,7 @@ final class WorkspaceTests: XCTestCase { } XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .local) result.check(dependency: "bar", at: .checkout(.version("1.0.0"))) } @@ -2893,7 +2893,7 @@ final class WorkspaceTests: XCTestCase { } XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "bar", at: .checkout(.version("1.0.0"))) } @@ -2931,7 +2931,7 @@ final class WorkspaceTests: XCTestCase { try await workspace.checkPackageGraph(roots: ["Foo"]) { _, diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "bar", at: .checkout(.version("1.5.0"))) } } @@ -2990,7 +2990,7 @@ final class WorkspaceTests: XCTestCase { await workspace.checkEdit(packageIdentity: "Foo") { diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .edited(nil)) } workspace.checkResolved { result in @@ -3078,15 +3078,15 @@ final class WorkspaceTests: XCTestCase { do { try fs.writeFileContents(fooEditPath.appending("Package.swift"), string: "// swift-tools-version: 5.6") - let fooState = underlying.state.dependencies[.plain("foo")]! - let externalState = WorkspaceState( + let fooState = await underlying.state.dependencies[.plain("foo")]! + let externalState = await WorkspaceState( fileSystem: fs, storageDirectory: underlying.state.storagePath.parentDirectory, initializationWarningHandler: { _ in } ) - externalState.dependencies.remove(fooState.packageRef.identity) - externalState.dependencies.add(try fooState.edited(subpath: "foo", unmanagedPath: fooEditPath)) - try externalState.save() + await externalState.dependencies.remove(fooState.packageRef.identity) + await externalState.dependencies.add(try fooState.edited(subpath: "foo", unmanagedPath: fooEditPath)) + try await externalState.save() } // reload graph after "external" change @@ -3097,7 +3097,7 @@ final class WorkspaceTests: XCTestCase { } do { - let fooState = underlying.state.dependencies[.plain("foo")]! + let fooState = await underlying.state.dependencies[.plain("foo")]! guard case .edited(basedOn: _, unmanagedPath: fooEditPath) = fooState.state else { XCTFail( "'\(fooState.packageRef.identity)' dependency expected to be in edit mode, but was: \(fooState)" @@ -3216,7 +3216,7 @@ final class WorkspaceTests: XCTestCase { } XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "baz", at: .checkout(.version("1.5.0"))) result.check(dependency: "bar", at: .local) } @@ -3336,7 +3336,7 @@ final class WorkspaceTests: XCTestCase { } XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "bar", at: .checkout(.version("1.5.0"))) } @@ -3347,7 +3347,7 @@ final class WorkspaceTests: XCTestCase { try await workspace.checkUpdate(roots: ["Foo"], deps: deps) { diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "bar", at: .local) } @@ -3355,7 +3355,7 @@ final class WorkspaceTests: XCTestCase { try await workspace.checkUpdate(roots: ["Foo"]) { diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "bar", at: .checkout(.version("1.5.0"))) } } @@ -3444,7 +3444,7 @@ final class WorkspaceTests: XCTestCase { } XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.branch("develop"))) } @@ -3454,7 +3454,7 @@ final class WorkspaceTests: XCTestCase { try await workspace.checkPackageGraph(roots: ["Root"], deps: deps) { _, diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.0.0"))) } @@ -3464,7 +3464,7 @@ final class WorkspaceTests: XCTestCase { try await workspace.checkPackageGraph(roots: ["Root"], deps: deps) { _, diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.branch("develop"))) } } @@ -3513,7 +3513,7 @@ final class WorkspaceTests: XCTestCase { } XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .local) } @@ -3523,7 +3523,7 @@ final class WorkspaceTests: XCTestCase { try await workspace.checkPackageGraph(roots: ["Root"], deps: deps) { _, diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.0.0"))) } @@ -3533,7 +3533,7 @@ final class WorkspaceTests: XCTestCase { try await workspace.checkPackageGraph(roots: ["Root"], deps: deps) { _, diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .local) } } @@ -3593,7 +3593,7 @@ final class WorkspaceTests: XCTestCase { } XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .local) } @@ -3603,7 +3603,7 @@ final class WorkspaceTests: XCTestCase { try await workspace.checkPackageGraph(roots: ["Root"], deps: deps) { _, diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo2", at: .local) } } @@ -3662,7 +3662,7 @@ final class WorkspaceTests: XCTestCase { } XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .local) XCTAssertEqual( result.managedDependencies[.plain("foo")]?.packageRef.locationString, @@ -3676,7 +3676,7 @@ final class WorkspaceTests: XCTestCase { try await workspace.checkPackageGraph(roots: ["Root"], deps: deps) { _, diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .local) XCTAssertEqual( result.managedDependencies[.plain("foo")]?.packageRef.locationString, @@ -3740,12 +3740,13 @@ final class WorkspaceTests: XCTestCase { result.check(packages: "Foo", "Root") } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.0.0"))) } do { let ws = try workspace.getOrCreateWorkspace() - XCTAssertEqual(ws.state.dependencies[.plain("foo")]?.packageRef.locationString, "https://scm.com/org/foo") + let locationString = await ws.state.dependencies[.plain("foo")]?.packageRef.locationString + XCTAssertEqual(locationString, "https://scm.com/org/foo") } deps = [ @@ -3754,12 +3755,13 @@ final class WorkspaceTests: XCTestCase { try await workspace.checkPackageGraph(roots: ["Root"], deps: deps) { _, diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.1.0"))) } do { let ws = try workspace.getOrCreateWorkspace() - XCTAssertEqual(ws.state.dependencies[.plain("foo")]?.packageRef.locationString, "https://scm.com/other/foo") + let locationString = await ws.state.dependencies[.plain("foo")]?.packageRef.locationString + XCTAssertEqual(locationString, "https://scm.com/other/foo") } } @@ -3800,7 +3802,7 @@ final class WorkspaceTests: XCTestCase { try await workspace.checkPackageGraph(roots: ["Root"], deps: deps) { _, diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.0.0"))) } workspace.checkResolved { result in @@ -3810,7 +3812,7 @@ final class WorkspaceTests: XCTestCase { try await workspace.checkPackageGraph(roots: ["Root"], deps: []) { _, diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.0.0"))) } workspace.checkResolved { result in @@ -3873,7 +3875,7 @@ final class WorkspaceTests: XCTestCase { try await workspace.checkPackageGraph(roots: ["Root1", "Root2"]) { _, diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.0.0"))) } workspace.checkResolved { result in @@ -3976,7 +3978,7 @@ final class WorkspaceTests: XCTestCase { try await workspace.checkPackageGraph(roots: ["Root"], deps: deps) { _, diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.0.0"))) result.check(dependency: "bar", at: .checkout(.version("1.0.0"))) XCTAssertEqual( @@ -4003,14 +4005,14 @@ final class WorkspaceTests: XCTestCase { ] // reset state, excluding the resolved file - try workspace.closeWorkspace(resetResolvedFile: false) + try await workspace.closeWorkspace(resetResolvedFile: false) XCTAssertTrue(fs.exists(sandbox.appending("Package.resolved"))) // run update try await workspace.checkUpdate(roots: ["Root"], deps: deps) { diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.0.0"))) result.check(dependency: "bar", at: .checkout(.version("1.0.0"))) // URLs should reflect the actual dependencies @@ -4038,14 +4040,14 @@ final class WorkspaceTests: XCTestCase { .sourceControl(url: "https://localhost/org/bar.git", requirement: .exact("1.1.0")), ] // reset state, excluding the resolved file - try workspace.closeWorkspace(resetResolvedFile: false) + try await workspace.closeWorkspace(resetResolvedFile: false) XCTAssertTrue(fs.exists(sandbox.appending("Package.resolved"))) // run update try await workspace.checkUpdate(roots: ["Root"], deps: deps) { diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.1.0"))) result.check(dependency: "bar", at: .checkout(.version("1.1.0"))) // URLs should reflect the actual dependencies @@ -4076,7 +4078,7 @@ final class WorkspaceTests: XCTestCase { .sourceControl(url: "https://localhost/org/bar.git", requirement: .exact("1.0.0")), ] // reset state, including the resolved file - workspace.checkReset { XCTAssertNoDiagnostics($0) } + await workspace.checkReset { XCTAssertNoDiagnostics($0) } try fs.removeFileTree(sandbox.appending("Package.resolved")) XCTAssertFalse(fs.exists(sandbox.appending("Package.resolved"))) // run update @@ -4084,7 +4086,7 @@ final class WorkspaceTests: XCTestCase { XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.0.0"))) result.check(dependency: "bar", at: .checkout(.version("1.0.0"))) // URLs should reflect the actual dependencies @@ -4207,7 +4209,7 @@ final class WorkspaceTests: XCTestCase { do { // reset but keep resolved file - try workspace.closeWorkspace(resetResolvedFile: false) + try await workspace.closeWorkspace(resetResolvedFile: false) // run resolution again, now it should rely on the resolved file try await workspace.checkPackageGraph(roots: ["Root"]) { graph, diagnostics in PackageGraphTester(graph) { result in @@ -4228,7 +4230,7 @@ final class WorkspaceTests: XCTestCase { do { // reset but keep resolved file - try workspace.closeWorkspace(resetResolvedFile: false) + try await workspace.closeWorkspace(resetResolvedFile: false) // change the manifest let rootManifestPath = try workspace.pathToRoot(withName: "Root").appending(Manifest.filename) let manifestContent: String = try fs.readFileContents(rootManifestPath) @@ -4256,7 +4258,7 @@ final class WorkspaceTests: XCTestCase { } // reset but keep resolved file - try workspace.closeWorkspace(resetResolvedFile: false) + try await workspace.closeWorkspace(resetResolvedFile: false) // restore original manifest try fs.writeFileContents(rootManifestPath, string: manifestContent) // run resolution again, now it should rely on the resolved file @@ -4279,7 +4281,7 @@ final class WorkspaceTests: XCTestCase { do { // reset but keep resolved file - try workspace.closeWorkspace(resetResolvedFile: false) + try await workspace.closeWorkspace(resetResolvedFile: false) // change the dependency requirements let changedDeps: [PackageDependency] = [ .remoteSourceControl(url: "https://localhost/org/baz", requirement: .upToNextMinor(from: "1.0.0")) @@ -4306,7 +4308,7 @@ final class WorkspaceTests: XCTestCase { } // reset but keep resolved file - try workspace.closeWorkspace(resetResolvedFile: false) + try await workspace.closeWorkspace(resetResolvedFile: false) // run resolution again, but change requirements back to original try await workspace.checkPackageGraph(roots: ["Root"]) { graph, diagnostics in PackageGraphTester(graph) { result in @@ -4329,7 +4331,7 @@ final class WorkspaceTests: XCTestCase { } // reset but keep resolved file - try workspace.closeWorkspace(resetResolvedFile: false) + try await workspace.closeWorkspace(resetResolvedFile: false) // run resolution again, now it should rely on the resolved file try await workspace.checkPackageGraph(roots: ["Root"]) { graph, diagnostics in PackageGraphTester(graph) { result in @@ -4350,7 +4352,7 @@ final class WorkspaceTests: XCTestCase { do { // reset including removing resolved file - try workspace.closeWorkspace(resetResolvedFile: true) + try await workspace.closeWorkspace(resetResolvedFile: true) // run resolution again try await workspace.checkPackageGraph(roots: ["Root"]) { graph, diagnostics in PackageGraphTester(graph) { result in @@ -4463,7 +4465,7 @@ final class WorkspaceTests: XCTestCase { } XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "dep", at: .checkout(.version("1.4.0"))) result.check(dependency: "barmirror", at: .checkout(.version("1.5.0"))) result.check(dependency: "bazmirror", at: .checkout(.version("1.6.0"))) @@ -4567,7 +4569,7 @@ final class WorkspaceTests: XCTestCase { } XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "dep", at: .checkout(.version("1.4.0"))) result.check(dependency: "barmirror", at: .checkout(.version("1.5.0"))) result.check(notPresent: "baz") @@ -4655,7 +4657,7 @@ final class WorkspaceTests: XCTestCase { } XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "dep", at: .checkout(.version("1.4.0"))) result.check(dependency: "bar-mirror", at: .checkout(.version("1.5.0"))) result.check(dependency: "baz-mirror", at: .checkout(.version("1.6.0"))) @@ -4761,7 +4763,7 @@ final class WorkspaceTests: XCTestCase { } XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "dep", at: .checkout(.version("1.4.0"))) result.check(dependency: "bar-mirror", at: .checkout(.version("1.5.0"))) result.check(notPresent: "bar") @@ -4832,7 +4834,7 @@ final class WorkspaceTests: XCTestCase { } XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "org.bar-mirror", at: .registryDownload("1.5.0")) result.check(dependency: "baz", at: .checkout(.version("1.6.0"))) result.check(notPresent: "bar") @@ -4902,7 +4904,7 @@ final class WorkspaceTests: XCTestCase { } XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "bar-mirror", at: .checkout(.version("1.5.0"))) result.check(dependency: "org.baz", at: .registryDownload("1.6.0")) result.check(notPresent: "org.bar") @@ -5017,7 +5019,7 @@ final class WorkspaceTests: XCTestCase { result.check(packages: "Bar", "Foo", "Root") } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.0.0"))) result.check(dependency: "bar", at: .checkout(.version("1.0.0"))) XCTAssertEqual( @@ -5032,7 +5034,7 @@ final class WorkspaceTests: XCTestCase { } // reset state - workspace.checkReset { XCTAssertNoDiagnostics($0) } + await workspace.checkReset { XCTAssertNoDiagnostics($0) } deps = [ .sourceControl(url: "https://scm.com/org/bar", requirement: .exact("1.1.0")), @@ -5044,7 +5046,7 @@ final class WorkspaceTests: XCTestCase { result.check(packages: "Bar", "Foo", "Root") } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.0.0"))) result.check(dependency: "bar", at: .checkout(.version("1.1.0"))) XCTAssertEqual( @@ -5110,7 +5112,7 @@ final class WorkspaceTests: XCTestCase { try await workspace.checkPackageGraph(roots: ["Root"], deps: deps) { _, diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.3.2"))) result.check(dependency: "bar", at: .checkout(.branch("develop"))) } @@ -5146,7 +5148,7 @@ final class WorkspaceTests: XCTestCase { ) } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.0.0"))) result.check(dependency: "bar", at: .checkout(.branch("develop"))) } @@ -5159,7 +5161,7 @@ final class WorkspaceTests: XCTestCase { try await workspace.checkPackageGraph(roots: ["Root"]) { _, diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.0.0"))) result.check(dependency: "bar", at: .checkout(.version("1.0.0"))) } @@ -5172,7 +5174,7 @@ final class WorkspaceTests: XCTestCase { try await workspace.checkPackageGraph(roots: ["Root"], forceResolvedVersions: true) { _, diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.0.0"))) result.check(dependency: "bar", at: .checkout(.version("1.0.0"))) } @@ -5326,7 +5328,7 @@ final class WorkspaceTests: XCTestCase { try await workspace.checkPackageGraph(roots: ["Root"], forceResolvedVersions: true) { _, diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .local) } } @@ -5365,7 +5367,7 @@ final class WorkspaceTests: XCTestCase { try await workspace.checkPackageGraph(roots: ["Root"], dependencies: [.fileSystem(path: workspace.packagesDir.appending(component: "Foo"))], forceResolvedVersions: true) { _, diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .local) } } @@ -5617,7 +5619,7 @@ final class WorkspaceTests: XCTestCase { ) } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "bar", at: .checkout(.version("1.0.0"))) result.check(dependency: "baz", at: .checkout(.version("1.0.0"))) XCTAssertEqual( @@ -5818,7 +5820,7 @@ final class WorkspaceTests: XCTestCase { try await workspace.checkPackageGraph(roots: ["Root"]) { _, diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.branch("master"))) result.check(dependency: "bar", at: .checkout(.branch("master"))) result.check(dependency: "baz", at: .checkout(.version("1.0.0"))) @@ -5829,7 +5831,7 @@ final class WorkspaceTests: XCTestCase { await workspace.checkEdit(packageIdentity: "Foo") { diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .edited(nil)) } XCTAssertTrue(fs.exists(fooPath)) @@ -5847,7 +5849,7 @@ final class WorkspaceTests: XCTestCase { try await workspace.checkPackageGraph(roots: ["Root"]) { _, diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .edited(nil)) result.check(dependency: "bar", at: .checkout(.branch("master"))) result.check(dependency: "baz", at: .checkout(.version("1.0.0"))) @@ -5952,7 +5954,7 @@ final class WorkspaceTests: XCTestCase { try await workspace.checkPackageGraph(roots: ["Root"]) { _, diagnostics in XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.0.0"))) result.check(dependency: "bar", at: .checkout(.version("1.0.0"))) result.check(dependency: "baz", at: .checkout(.version("1.0.0"))) @@ -6097,7 +6099,7 @@ final class WorkspaceTests: XCTestCase { XCTAssertTrue(try! fs.getDirectoryContents(AbsolutePath("/tmp/ws/.build/artifacts/extract/b/B")).isEmpty) } - workspace.checkManagedArtifacts { result in + await workspace.checkManagedArtifacts { result in result.check( packageIdentity: .plain("a"), targetName: "A1", @@ -6287,7 +6289,7 @@ final class WorkspaceTests: XCTestCase { let aState = CheckoutState.version("1.0.0", revision: aRevision) // Set an initial workspace state - try workspace.set( + try await workspace.set( resolvedPackages: [aRef: aState], managedArtifacts: [ .init( @@ -6375,7 +6377,7 @@ final class WorkspaceTests: XCTestCase { XCTAssertFalse(fs.exists(try AbsolutePath(validating: "/tmp/ws/.build/artifacts/a/A5/\(a5FrameworkName)"))) } - workspace.checkManagedArtifacts { result in + await workspace.checkManagedArtifacts { result in result.check( packageIdentity: .plain("a"), targetName: "A1", @@ -6572,7 +6574,7 @@ final class WorkspaceTests: XCTestCase { XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedArtifacts { result in + await workspace.checkManagedArtifacts { result in result.check( packageIdentity: .plain("root"), targetName: "A1", @@ -6640,7 +6642,7 @@ final class WorkspaceTests: XCTestCase { let rootRef = PackageReference.root(identity: PackageIdentity(path: rootPath), path: rootPath) // Set an initial workspace state - try workspace.set( + try await workspace.set( managedArtifacts: [ .init( packageRef: rootRef, @@ -6676,7 +6678,7 @@ final class WorkspaceTests: XCTestCase { ]) } - workspace.checkManagedArtifacts { result in + await workspace.checkManagedArtifacts { result in result.check( packageIdentity: .plain("root"), targetName: "A1", @@ -6780,7 +6782,7 @@ final class WorkspaceTests: XCTestCase { ]) } - workspace.checkManagedArtifacts { result in + await workspace.checkManagedArtifacts { result in result.check( packageIdentity: .plain("root"), targetName: "flat", @@ -6838,7 +6840,7 @@ final class WorkspaceTests: XCTestCase { XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedArtifacts { result in + await workspace.checkManagedArtifacts { result in result.check( packageIdentity: .plain("root"), targetName: "A1", @@ -7043,7 +7045,7 @@ final class WorkspaceTests: XCTestCase { ) } - workspace.checkManagedArtifacts { result in + await workspace.checkManagedArtifacts { result in result.check( packageIdentity: .plain("a"), targetName: "A1", @@ -7248,7 +7250,7 @@ final class WorkspaceTests: XCTestCase { let aRevision = try aRepo.resolveRevision(tag: "1.0.0") let aState = CheckoutState.version("1.0.0", revision: aRevision) - try workspace.set( + try await workspace.set( resolvedPackages: [aRef: aState], managedArtifacts: [ .init( @@ -7347,7 +7349,7 @@ final class WorkspaceTests: XCTestCase { ) } - workspace.checkManagedArtifacts { result in + await workspace.checkManagedArtifacts { result in result.check( packageIdentity: .plain("a"), targetName: "A1", @@ -7491,7 +7493,7 @@ final class WorkspaceTests: XCTestCase { // reset - try workspace.resetState() + try await workspace.resetState() // do it again @@ -7914,7 +7916,7 @@ final class WorkspaceTests: XCTestCase { XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedArtifacts { result in + await workspace.checkManagedArtifacts { result in result.check( packageIdentity: .plain("root"), targetName: "A1", @@ -8026,7 +8028,7 @@ final class WorkspaceTests: XCTestCase { let rootPath = try workspace.pathToRoot(withName: "Root") let rootRef = PackageReference.root(identity: PackageIdentity(path: rootPath), path: rootPath) - try workspace.set( + try await workspace.set( managedArtifacts: [ .init( packageRef: rootRef, @@ -8122,7 +8124,7 @@ final class WorkspaceTests: XCTestCase { let rootPath = try workspace.pathToRoot(withName: "Root") let rootRef = PackageReference.root(identity: PackageIdentity(path: rootPath), path: rootPath) - try workspace.set( + try await workspace.set( managedArtifacts: [ .init( packageRef: rootRef, @@ -8300,7 +8302,7 @@ final class WorkspaceTests: XCTestCase { } // resetting state, should not come from global cache - try workspace.resetState() + try await workspace.resetState() try await workspace.checkPackageGraph(roots: ["Root"]) { _, diagnostics in XCTAssertNoDiagnostics(diagnostics) XCTAssertEqual(downloads.get(), 2) @@ -8389,14 +8391,14 @@ final class WorkspaceTests: XCTestCase { } // resetting state, should come from global cache - try workspace.resetState() + try await workspace.resetState() try await workspace.checkPackageGraph(roots: ["Root"]) { _, diagnostics in XCTAssertNoDiagnostics(diagnostics) XCTAssertEqual(downloads.get(), 1) } // delete global cache, should download again - try workspace.resetState() + try await workspace.resetState() try fs.removeFileTree(fs.swiftPMCacheDirectory) try await workspace.checkPackageGraph(roots: ["Root"]) { _, diagnostics in XCTAssertNoDiagnostics(diagnostics) @@ -8404,7 +8406,7 @@ final class WorkspaceTests: XCTestCase { } // resetting state, should come from global cache again - try workspace.resetState() + try await workspace.resetState() try await workspace.checkPackageGraph(roots: ["Root"]) { _, diagnostics in XCTAssertNoDiagnostics(diagnostics) XCTAssertEqual(downloads.get(), 2) @@ -8568,7 +8570,7 @@ final class WorkspaceTests: XCTestCase { ) } - workspace.checkManagedArtifacts { result in + await workspace.checkManagedArtifacts { result in result.check( packageIdentity: .plain("a"), targetName: "A", @@ -8679,7 +8681,7 @@ final class WorkspaceTests: XCTestCase { XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedArtifacts { result in + await workspace.checkManagedArtifacts { result in result.check( packageIdentity: .plain("root"), targetName: "binary", @@ -8784,7 +8786,7 @@ final class WorkspaceTests: XCTestCase { XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedArtifacts { result in + await workspace.checkManagedArtifacts { result in for package in packages { let targetName = package.targets.first!.name result.check( @@ -8933,7 +8935,7 @@ final class WorkspaceTests: XCTestCase { ) } - workspace.checkManagedArtifacts { result in + await workspace.checkManagedArtifacts { result in result.check( packageIdentity: .plain("root"), targetName: "flat", @@ -9066,7 +9068,7 @@ final class WorkspaceTests: XCTestCase { ) } - workspace.checkManagedArtifacts { result in + await workspace.checkManagedArtifacts { result in result.check( packageIdentity: .plain("root"), targetName: "A1", @@ -9330,7 +9332,7 @@ final class WorkspaceTests: XCTestCase { ) } - workspace.checkManagedArtifacts { result in + await workspace.checkManagedArtifacts { result in result.check( packageIdentity: .plain("a"), targetName: "A1", @@ -9507,7 +9509,7 @@ final class WorkspaceTests: XCTestCase { let rootPath = try workspace.pathToRoot(withName: "Root") let rootRef = PackageReference.root(identity: PackageIdentity(path: rootPath), path: rootPath) - try workspace.set( + try await workspace.set( managedArtifacts: [ .init( packageRef: rootRef, @@ -11476,7 +11478,7 @@ final class WorkspaceTests: XCTestCase { } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in XCTAssertEqual(result.managedDependencies["foo"]?.packageRef.locationString, "https://github.com/org/foo.git") } @@ -11613,7 +11615,7 @@ final class WorkspaceTests: XCTestCase { } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in XCTAssertEqual(result.managedDependencies["foo"]?.packageRef.locationString, "git@github.com:org/foo.git") } @@ -11743,7 +11745,7 @@ final class WorkspaceTests: XCTestCase { } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in XCTAssertEqual(result.managedDependencies["foo"]?.packageRef.locationString, "git@github.com:org/foo.git") } @@ -11768,7 +11770,7 @@ final class WorkspaceTests: XCTestCase { } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in // we expect the managed dependency to carry the old state XCTAssertEqual(result.managedDependencies["foo"]?.packageRef.locationString, "git@github.com:org/foo.git") } @@ -11854,7 +11856,7 @@ final class WorkspaceTests: XCTestCase { } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in XCTAssertEqual(result.managedDependencies["foo"]?.packageRef.locationString, "https://github.com/org/foo.git") } @@ -11874,7 +11876,7 @@ final class WorkspaceTests: XCTestCase { } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in XCTAssertEqual(result.managedDependencies["foo"]?.packageRef.locationString, "git@github.com:org/foo.git") } @@ -12007,7 +12009,7 @@ final class WorkspaceTests: XCTestCase { } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in XCTAssertEqual(result.managedDependencies["foo"]?.packageRef.locationString, "https://github.com/org/foo.git") } @@ -12033,7 +12035,7 @@ final class WorkspaceTests: XCTestCase { } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in XCTAssertEqual(result.managedDependencies["foo"]?.packageRef.locationString, "git@github.com:org/foo.git") } @@ -12223,7 +12225,7 @@ final class WorkspaceTests: XCTestCase { } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.branch("experiment"))) result.check(dependency: "bar", at: .checkout(.version("1.0.0"))) } @@ -12440,7 +12442,7 @@ final class WorkspaceTests: XCTestCase { } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.5.1"))) result.check(dependency: "bar", at: .checkout(.version("2.2.0"))) } @@ -12575,7 +12577,7 @@ final class WorkspaceTests: XCTestCase { } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.1.0"))) result.check(dependency: "bar", at: .checkout(.version("2.1.0"))) result.check(dependency: "baz", at: .checkout(.version("3.1.0"))) @@ -12690,7 +12692,7 @@ final class WorkspaceTests: XCTestCase { } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "org.foo", at: .registryDownload("1.5.1")) result.check(dependency: "org.bar", at: .registryDownload("2.2.0")) } @@ -12825,7 +12827,7 @@ final class WorkspaceTests: XCTestCase { } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "org.foo", at: .registryDownload("1.1.0")) result.check(dependency: "org.bar", at: .registryDownload("2.1.0")) result.check(dependency: "org.baz", at: .registryDownload("3.1.0")) @@ -12943,7 +12945,7 @@ final class WorkspaceTests: XCTestCase { } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "org.foo", at: .registryDownload("1.2.0")) result.check(dependency: "org.bar", at: .registryDownload("1.1.0")) } @@ -13027,14 +13029,14 @@ final class WorkspaceTests: XCTestCase { } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "foo", at: .checkout(.version("1.2.0"))) result.check(dependency: "org.bar", at: .registryDownload("1.1.0")) } } // reset - try workspace.closeWorkspace() + try await workspace.closeWorkspace() do { workspace.sourceControlToRegistryDependencyTransformation = .identity @@ -13050,14 +13052,14 @@ final class WorkspaceTests: XCTestCase { } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "org.foo", at: .checkout(.version("1.2.0"))) result.check(dependency: "org.bar", at: .registryDownload("1.1.0")) } } // reset - try workspace.closeWorkspace() + try await workspace.closeWorkspace() do { workspace.sourceControlToRegistryDependencyTransformation = .swizzle @@ -13073,7 +13075,7 @@ final class WorkspaceTests: XCTestCase { } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "org.foo", at: .registryDownload("1.2.0")) result.check(dependency: "org.bar", at: .registryDownload("1.1.0")) } @@ -13143,7 +13145,7 @@ final class WorkspaceTests: XCTestCase { } // reset - try workspace.closeWorkspace() + try await workspace.closeWorkspace() do { workspace.sourceControlToRegistryDependencyTransformation = .identity @@ -13159,7 +13161,7 @@ final class WorkspaceTests: XCTestCase { } // reset - try workspace.closeWorkspace() + try await workspace.closeWorkspace() do { workspace.sourceControlToRegistryDependencyTransformation = .swizzle @@ -13261,7 +13263,7 @@ final class WorkspaceTests: XCTestCase { } // reset - try workspace.closeWorkspace() + try await workspace.closeWorkspace() do { workspace.sourceControlToRegistryDependencyTransformation = .identity @@ -13285,14 +13287,14 @@ final class WorkspaceTests: XCTestCase { } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "org.foo", at: .checkout(.version("1.1.0"))) result.check(dependency: "bar", at: .checkout(.version("1.1.0"))) } } // reset - try workspace.closeWorkspace() + try await workspace.closeWorkspace() do { workspace.sourceControlToRegistryDependencyTransformation = .swizzle @@ -13309,14 +13311,14 @@ final class WorkspaceTests: XCTestCase { } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "org.foo", at: .registryDownload("1.1.0")) result.check(dependency: "bar", at: .checkout(.version("1.1.0"))) } } // reset - try workspace.closeWorkspace() + try await workspace.closeWorkspace() do { workspace.sourceControlToRegistryDependencyTransformation = .swizzle @@ -13332,7 +13334,7 @@ final class WorkspaceTests: XCTestCase { } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "org.foo", at: .registryDownload("1.1.0")) result.check(dependency: "bar", at: .checkout(.version("1.1.0"))) } @@ -13424,7 +13426,7 @@ final class WorkspaceTests: XCTestCase { } // reset - try workspace.closeWorkspace() + try await workspace.closeWorkspace() do { workspace.sourceControlToRegistryDependencyTransformation = .identity @@ -13447,14 +13449,14 @@ final class WorkspaceTests: XCTestCase { } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "org.foo", at: .checkout(.version("1.2.0"))) result.check(dependency: "org.bar", at: .registryDownload("1.1.0")) } } // reset - try workspace.closeWorkspace() + try await workspace.closeWorkspace() do { workspace.sourceControlToRegistryDependencyTransformation = .swizzle @@ -13470,7 +13472,7 @@ final class WorkspaceTests: XCTestCase { } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "org.foo", at: .registryDownload("1.2.0")) result.check(dependency: "org.bar", at: .registryDownload("1.1.0")) } @@ -13562,7 +13564,7 @@ final class WorkspaceTests: XCTestCase { } // reset - try workspace.closeWorkspace() + try await workspace.closeWorkspace() do { workspace.sourceControlToRegistryDependencyTransformation = .identity @@ -13582,7 +13584,7 @@ final class WorkspaceTests: XCTestCase { } // reset - try workspace.closeWorkspace() + try await workspace.closeWorkspace() do { workspace.sourceControlToRegistryDependencyTransformation = .swizzle @@ -13687,7 +13689,7 @@ final class WorkspaceTests: XCTestCase { } // reset - try workspace.closeWorkspace() + try await workspace.closeWorkspace() do { workspace.sourceControlToRegistryDependencyTransformation = .identity @@ -13718,14 +13720,14 @@ final class WorkspaceTests: XCTestCase { } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "org.foo", at: .registryDownload("1.2.0")) result.check(dependency: "org.bar", at: .registryDownload("1.1.0")) } } // reset - try workspace.closeWorkspace() + try await workspace.closeWorkspace() do { workspace.sourceControlToRegistryDependencyTransformation = .swizzle @@ -13741,7 +13743,7 @@ final class WorkspaceTests: XCTestCase { } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "org.foo", at: .registryDownload("1.2.0")) result.check(dependency: "org.bar", at: .registryDownload("1.1.0")) } @@ -13833,7 +13835,7 @@ final class WorkspaceTests: XCTestCase { } // reset - try workspace.closeWorkspace() + try await workspace.closeWorkspace() do { workspace.sourceControlToRegistryDependencyTransformation = .identity @@ -13853,7 +13855,7 @@ final class WorkspaceTests: XCTestCase { } // reset - try workspace.closeWorkspace() + try await workspace.closeWorkspace() do { workspace.sourceControlToRegistryDependencyTransformation = .swizzle @@ -13974,7 +13976,7 @@ final class WorkspaceTests: XCTestCase { } // reset - try workspace.closeWorkspace() + try await workspace.closeWorkspace() do { workspace.sourceControlToRegistryDependencyTransformation = .identity @@ -14009,7 +14011,7 @@ final class WorkspaceTests: XCTestCase { } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "org.foo", at: .registryDownload("1.0.0")) result.check(dependency: "org.bar", at: .registryDownload("1.0.0")) result.check(dependency: "org.baz", at: .registryDownload("1.1.0")) @@ -14017,7 +14019,7 @@ final class WorkspaceTests: XCTestCase { } // reset - try workspace.closeWorkspace() + try await workspace.closeWorkspace() do { workspace.sourceControlToRegistryDependencyTransformation = .swizzle @@ -14035,7 +14037,7 @@ final class WorkspaceTests: XCTestCase { } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "org.foo", at: .registryDownload("1.0.0")) result.check(dependency: "org.bar", at: .registryDownload("1.0.0")) result.check(dependency: "org.baz", at: .registryDownload("1.1.0")) @@ -14144,7 +14146,7 @@ final class WorkspaceTests: XCTestCase { } // reset - try workspace.closeWorkspace() + try await workspace.closeWorkspace() do { workspace.sourceControlToRegistryDependencyTransformation = .identity @@ -14164,7 +14166,7 @@ final class WorkspaceTests: XCTestCase { } // reset - try workspace.closeWorkspace() + try await workspace.closeWorkspace() do { workspace.sourceControlToRegistryDependencyTransformation = .swizzle @@ -14269,7 +14271,7 @@ final class WorkspaceTests: XCTestCase { } // reset - try workspace.closeWorkspace() + try await workspace.closeWorkspace() do { workspace.sourceControlToRegistryDependencyTransformation = .identity @@ -14292,14 +14294,14 @@ final class WorkspaceTests: XCTestCase { } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "org.foo", at: .checkout(.branch("experiment"))) result.check(dependency: "org.bar", at: .registryDownload("1.0.0")) } } // reset - try workspace.closeWorkspace() + try await workspace.closeWorkspace() do { workspace.sourceControlToRegistryDependencyTransformation = .swizzle @@ -14322,7 +14324,7 @@ final class WorkspaceTests: XCTestCase { } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result .check( dependency: "org.foo", @@ -14417,7 +14419,7 @@ final class WorkspaceTests: XCTestCase { } XCTAssertNoDiagnostics(diagnostics) } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "baz", at: .custom(Version(1, 0, 0), .root)) } } @@ -14523,7 +14525,7 @@ final class WorkspaceTests: XCTestCase { fileSystem: fs ) - try workspace.closeWorkspace() + try await workspace.closeWorkspace() workspace.registryClient = registryClient await workspace.checkPackageGraphFailure(roots: ["MyPackage"]) { diagnostics in testDiagnostics(diagnostics) { result in @@ -14545,7 +14547,7 @@ final class WorkspaceTests: XCTestCase { fileSystem: fs ) - try workspace.closeWorkspace() + try await workspace.closeWorkspace() workspace.registryClient = registryClient await workspace.checkPackageGraphFailure(roots: ["MyPackage"]) { diagnostics in testDiagnostics(diagnostics) { result in @@ -14608,7 +14610,7 @@ final class WorkspaceTests: XCTestCase { fileSystem: fs ) - try workspace.closeWorkspace() + try await workspace.closeWorkspace() workspace.registryClient = registryClient await workspace.checkPackageGraphFailure(roots: ["MyPackage"]) { diagnostics in testDiagnostics(diagnostics) { result in @@ -14632,7 +14634,7 @@ final class WorkspaceTests: XCTestCase { fileSystem: fs ) - try workspace.closeWorkspace() + try await workspace.closeWorkspace() workspace.registryClient = registryClient await workspace.checkPackageGraphFailure(roots: ["MyPackage"]) { diagnostics in testDiagnostics(diagnostics) { result in @@ -14695,7 +14697,7 @@ final class WorkspaceTests: XCTestCase { fileSystem: fs ) - try workspace.closeWorkspace() + try await workspace.closeWorkspace() workspace.registryClient = registryClient await workspace.checkPackageGraphFailure(roots: ["MyPackage"]) { diagnostics in testDiagnostics(diagnostics) { result in @@ -14717,7 +14719,7 @@ final class WorkspaceTests: XCTestCase { fileSystem: fs ) - try workspace.closeWorkspace() + try await workspace.closeWorkspace() workspace.registryClient = registryClient await workspace.checkPackageGraphFailure(roots: ["MyPackage"]) { diagnostics in testDiagnostics(diagnostics) { result in @@ -14780,7 +14782,7 @@ final class WorkspaceTests: XCTestCase { fileSystem: fs ) - try workspace.closeWorkspace() + try await workspace.closeWorkspace() workspace.registryClient = registryClient await workspace.checkPackageGraphFailure(roots: ["MyPackage"]) { diagnostics in testDiagnostics(diagnostics) { result in @@ -14804,7 +14806,7 @@ final class WorkspaceTests: XCTestCase { fileSystem: fs ) - try workspace.closeWorkspace() + try await workspace.closeWorkspace() workspace.registryClient = registryClient await workspace.checkPackageGraphFailure(roots: ["MyPackage"]) { diagnostics in testDiagnostics(diagnostics) { result in @@ -14867,7 +14869,7 @@ final class WorkspaceTests: XCTestCase { registryClient: registryClient ) - try workspace.closeWorkspace() + try await workspace.closeWorkspace() workspace.registryClient = registryClient await workspace.checkPackageGraphFailure(roots: ["MyPackage"]) { diagnostics in testDiagnostics(diagnostics) { result in @@ -14958,7 +14960,7 @@ final class WorkspaceTests: XCTestCase { } } - workspace.checkManagedDependencies { result in + await workspace.checkManagedDependencies { result in result.check(dependency: "org.foo", at: .registryDownload("1.5.1")) } } From 0cf2d7ce9fb7c621f6ca240994df13fadc28931c Mon Sep 17 00:00:00 2001 From: fortmarek Date: Wed, 15 Jan 2025 09:35:50 +0100 Subject: [PATCH 2/4] Parallelize retrieving resolved packages --- .../Workspace/Workspace+Dependencies.swift | 45 ++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/Sources/Workspace/Workspace+Dependencies.swift b/Sources/Workspace/Workspace+Dependencies.swift index a113ed83bc3..2469073898c 100644 --- a/Sources/Workspace/Workspace+Dependencies.swift +++ b/Sources/Workspace/Workspace+Dependencies.swift @@ -427,26 +427,31 @@ extension Workspace { } // Retrieve the required resolved packages. - for resolvedPackage in requiredResolvedPackages { - await observabilityScope.makeChildScope( - description: "retrieving resolved package versions for dependencies", - metadata: resolvedPackage.packageRef.diagnosticsMetadata - ).trap { - switch resolvedPackage.packageRef.kind { - case .localSourceControl, .remoteSourceControl: - _ = try await self.checkoutRepository( - package: resolvedPackage.packageRef, - at: resolvedPackage.state, - observabilityScope: observabilityScope - ) - case .registry: - _ = try await self.downloadRegistryArchive( - package: resolvedPackage.packageRef, - at: resolvedPackage.state, - observabilityScope: observabilityScope - ) - default: - throw InternalError("invalid resolved package type \(resolvedPackage.packageRef.kind)") + await withThrowingTaskGroup(of: Void.self) { taskGroup in + for resolvedPackage in requiredResolvedPackages { + let observabilityScope = observabilityScope.makeChildScope( + description: "retrieving resolved package versions for dependencies", + metadata: resolvedPackage.packageRef.diagnosticsMetadata + ) + taskGroup.addTask { + await observabilityScope.trap { + switch resolvedPackage.packageRef.kind { + case .localSourceControl, .remoteSourceControl: + _ = try await self.checkoutRepository( + package: resolvedPackage.packageRef, + at: resolvedPackage.state, + observabilityScope: observabilityScope + ) + case .registry: + _ = try await self.downloadRegistryArchive( + package: resolvedPackage.packageRef, + at: resolvedPackage.state, + observabilityScope: observabilityScope + ) + default: + throw InternalError("invalid resolved package type \(resolvedPackage.packageRef.kind)") + } + } } } } From 3d99328874231fc3a0eb73b99cd3b7e76cc25164 Mon Sep 17 00:00:00 2001 From: fortmarek Date: Wed, 15 Jan 2025 10:48:53 +0100 Subject: [PATCH 3/4] Make mutating ManagedDependencies thread-safe --- Sources/Workspace/ManagedDependency.swift | 20 ++++++++++++++----- .../Workspace/Workspace+Dependencies.swift | 4 ++-- Sources/Workspace/Workspace+Editing.swift | 6 +++--- Sources/Workspace/Workspace+Manifests.swift | 4 ++-- Sources/Workspace/Workspace+Registry.swift | 4 ++-- .../Workspace/Workspace+SourceControl.swift | 4 ++-- Sources/Workspace/Workspace+State.swift | 8 ++++++++ Sources/Workspace/Workspace.swift | 6 +++--- .../_InternalTestSupport/MockWorkspace.swift | 2 +- Tests/WorkspaceTests/WorkspaceTests.swift | 4 ++-- 10 files changed, 40 insertions(+), 22 deletions(-) diff --git a/Sources/Workspace/ManagedDependency.swift b/Sources/Workspace/ManagedDependency.swift index b771f37221c..30d810c88c2 100644 --- a/Sources/Workspace/ManagedDependency.swift +++ b/Sources/Workspace/ManagedDependency.swift @@ -172,12 +172,18 @@ extension Workspace.ManagedDependency: CustomStringConvertible { extension Workspace { /// A collection of managed dependencies. - final public class ManagedDependencies { + public struct ManagedDependencies { private var dependencies: [PackageIdentity: ManagedDependency] init() { self.dependencies = [:] } + + private init( + _ dependencies: [PackageIdentity: ManagedDependency] + ) { + self.dependencies = dependencies + } init(_ dependencies: [ManagedDependency]) throws { // rdar://86857825 do not use Dictionary(uniqueKeysWithValues:) as it can crash the process when input is incorrect such as in older versions of SwiftPM @@ -204,12 +210,16 @@ extension Workspace { return .none } - public func add(_ dependency: ManagedDependency) { - self.dependencies[dependency.packageRef.identity] = dependency + public func add(_ dependency: ManagedDependency) -> Self { + var dependencies = dependencies + dependencies[dependency.packageRef.identity] = dependency + return ManagedDependencies(dependencies) } - public func remove(_ identity: PackageIdentity) { - self.dependencies[identity] = nil + public func remove(_ identity: PackageIdentity) -> Self { + var dependencies = dependencies + dependencies[identity] = nil + return ManagedDependencies(dependencies) } } } diff --git a/Sources/Workspace/Workspace+Dependencies.swift b/Sources/Workspace/Workspace+Dependencies.swift index 2469073898c..f80280e04c2 100644 --- a/Sources/Workspace/Workspace+Dependencies.swift +++ b/Sources/Workspace/Workspace+Dependencies.swift @@ -778,7 +778,7 @@ extension Workspace { state: .custom(version: version, path: path), subpath: RelativePath(validating: "") ) - await self.state.dependencies.add(dependency) + await self.state.add(dependency: dependency) try await self.state.save() return path } else { @@ -806,7 +806,7 @@ extension Workspace { throw InternalError("invalid package type: \(package.kind)") } - await self.state.dependencies.add(dependency) + await self.state.add(dependency: dependency) try await self.state.save() return path } diff --git a/Sources/Workspace/Workspace+Editing.swift b/Sources/Workspace/Workspace+Editing.swift index 6e3cdda2a48..c38036fc04c 100644 --- a/Sources/Workspace/Workspace+Editing.swift +++ b/Sources/Workspace/Workspace+Editing.swift @@ -157,8 +157,8 @@ extension Workspace { } // Save the new state. - try await self.state.dependencies.add( - dependency.edited(subpath: RelativePath(validating: packageIdentity), unmanagedPath: path) + try await self.state.add( + dependency: dependency.edited(subpath: RelativePath(validating: packageIdentity), unmanagedPath: path) ) try await self.state.save() } @@ -222,7 +222,7 @@ extension Workspace { ) } else { // The original dependency was removed, update the managed dependency state. - await self.state.dependencies.remove(dependency.packageRef.identity) + await self.state.remove(identity: dependency.packageRef.identity) try await self.state.save() } diff --git a/Sources/Workspace/Workspace+Manifests.swift b/Sources/Workspace/Workspace+Manifests.swift index 637c0b30bc7..f4d6d7d37cc 100644 --- a/Sources/Workspace/Workspace+Manifests.swift +++ b/Sources/Workspace/Workspace+Manifests.swift @@ -423,7 +423,7 @@ extension Workspace { // that would lead to loading the given package twice, once as a root and once as a dependency // which violates various assumptions. if case .fileSystem = ref.kind, !root.manifests.keys.contains(ref.identity) { - try await self.state.dependencies.add(.fileSystem(packageRef: ref)) + try await self.state.add(dependency: .fileSystem(packageRef: ref)) } } await observabilityScope.trap { try await self.state.save() } @@ -841,7 +841,7 @@ extension Workspace { .emit(.editedDependencyMissing(packageName: dependency.packageRef.identity.description)) case .fileSystem: - await self.state.dependencies.remove(dependency.packageRef.identity) + await self.state.remove(identity: dependency.packageRef.identity) try await self.state.save() } } diff --git a/Sources/Workspace/Workspace+Registry.swift b/Sources/Workspace/Workspace+Registry.swift index d81979c421b..f73a2dab860 100644 --- a/Sources/Workspace/Workspace+Registry.swift +++ b/Sources/Workspace/Workspace+Registry.swift @@ -424,8 +424,8 @@ extension Workspace { debug: "adding '\(package.identity)' (\(package.locationString)) to managed dependencies", metadata: package.diagnosticsMetadata ) - try await self.state.dependencies.add( - .registryDownload( + try await self.state.add( + dependency: .registryDownload( packageRef: package, version: version, subpath: downloadPath.relative(to: self.location.registryDownloadDirectory) diff --git a/Sources/Workspace/Workspace+SourceControl.swift b/Sources/Workspace/Workspace+SourceControl.swift index bd101b8c005..c2afe146c51 100644 --- a/Sources/Workspace/Workspace+SourceControl.swift +++ b/Sources/Workspace/Workspace+SourceControl.swift @@ -69,8 +69,8 @@ extension Workspace { debug: "adding '\(package.identity)' (\(package.locationString)) to managed dependencies", metadata: package.diagnosticsMetadata ) - try await self.state.dependencies.add( - .sourceControlCheckout( + try await self.state.add( + dependency: .sourceControlCheckout( packageRef: package, state: checkoutState, subpath: checkoutPath.relative(to: self.location.repositoriesCheckoutsDirectory) diff --git a/Sources/Workspace/Workspace+State.swift b/Sources/Workspace/Workspace+State.swift index 46f26925b43..adee2633287 100644 --- a/Sources/Workspace/Workspace+State.swift +++ b/Sources/Workspace/Workspace+State.swift @@ -88,6 +88,14 @@ public actor WorkspaceState { self.artifacts = storedState.artifacts self.prebuilts = storedState.prebuilts } + + public func add(dependency: Workspace.ManagedDependency) { + dependencies = dependencies.add(dependency) + } + + public func remove(identity: PackageIdentity) { + dependencies = dependencies.remove(identity) + } } // MARK: - Serialization diff --git a/Sources/Workspace/Workspace.swift b/Sources/Workspace/Workspace.swift index 8e0807b2c09..5423e17dc00 100644 --- a/Sources/Workspace/Workspace.swift +++ b/Sources/Workspace/Workspace.swift @@ -1302,7 +1302,7 @@ extension Workspace { // // Note that we don't actually remove a local package from disk. if case .fileSystem = dependency.state { - await self.state.dependencies.remove(package.identity) + await self.state.remove(identity: package.identity) try await self.state.save() return } @@ -1323,10 +1323,10 @@ extension Workspace { basedOn: .none, unmanagedPath: unmanagedPath ) - await self.state.dependencies.add(updatedDependency) + await self.state.add(dependency: updatedDependency) } else { dependencyToRemove = dependency - await self.state.dependencies.remove(dependencyToRemove.packageRef.identity) + await self.state.remove(identity: dependencyToRemove.packageRef.identity) } switch package.kind { diff --git a/Sources/_InternalTestSupport/MockWorkspace.swift b/Sources/_InternalTestSupport/MockWorkspace.swift index b32246b3739..de66dddd839 100644 --- a/Sources/_InternalTestSupport/MockWorkspace.swift +++ b/Sources/_InternalTestSupport/MockWorkspace.swift @@ -644,7 +644,7 @@ public final class MockWorkspace { } else { try self.fileSystem.createDirectory(managedPath, recursive: true) } - await workspace.state.dependencies.add(dependency.value) + await workspace.state.add(dependency: dependency.value) } for artifact in managedArtifacts { diff --git a/Tests/WorkspaceTests/WorkspaceTests.swift b/Tests/WorkspaceTests/WorkspaceTests.swift index 73558cf8fec..ad529eedc89 100644 --- a/Tests/WorkspaceTests/WorkspaceTests.swift +++ b/Tests/WorkspaceTests/WorkspaceTests.swift @@ -3084,8 +3084,8 @@ final class WorkspaceTests: XCTestCase { storageDirectory: underlying.state.storagePath.parentDirectory, initializationWarningHandler: { _ in } ) - await externalState.dependencies.remove(fooState.packageRef.identity) - await externalState.dependencies.add(try fooState.edited(subpath: "foo", unmanagedPath: fooEditPath)) + await externalState.remove(identity: fooState.packageRef.identity) + await externalState.add(dependency: try fooState.edited(subpath: "foo", unmanagedPath: fooEditPath)) try await externalState.save() } From 7bdd76e6a657fa65bb546419d55b059d2bc92c0e Mon Sep 17 00:00:00 2001 From: fortmarek Date: Sat, 18 Jan 2025 22:33:48 +0100 Subject: [PATCH 4/4] Change SwiftCommand conformance to AsyncParsableCommand --- Sources/CoreCommands/SwiftCommandState.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/CoreCommands/SwiftCommandState.swift b/Sources/CoreCommands/SwiftCommandState.swift index b2f85792d20..a5583f79047 100644 --- a/Sources/CoreCommands/SwiftCommandState.swift +++ b/Sources/CoreCommands/SwiftCommandState.swift @@ -98,7 +98,7 @@ extension _SwiftCommand { } } -public protocol SwiftCommand: ParsableCommand, _SwiftCommand { +public protocol SwiftCommand: AsyncParsableCommand, _SwiftCommand { func run(_ swiftCommandState: SwiftCommandState) async throws }