diff --git a/Sources/Basics/FileSystem+Extensions.swift b/Sources/Basics/FileSystem+Extensions.swift index 575cad505cd..0b5e2e9fefd 100644 --- a/Sources/Basics/FileSystem+Extensions.swift +++ b/Sources/Basics/FileSystem+Extensions.swift @@ -89,7 +89,7 @@ extension FileSystem { } extension FileSystem { - public func getOrCreateSwiftPMConfigurationDirectory(observabilityScope: ObservabilityScope?) throws -> AbsolutePath { + public func getOrCreateSwiftPMConfigurationDirectory() throws -> AbsolutePath { let idiomaticConfigurationDirectory = self.swiftPMConfigurationDirectory // temporary 5.6, remove on next version: transition from previous configuration location @@ -108,10 +108,12 @@ extension FileSystem { .filter{ self.isFile($0) && !self.isSymlink($0) && $0.extension != "lock"} for file in configurationFiles { let destination = idiomaticConfigurationDirectory.appending(component: file.basename) - observabilityScope?.emit(warning: "Usage of \(file) has been deprecated. Please delete it and use the new \(destination) instead.") if !self.exists(destination) { try self.copy(from: file, to: destination) } + // FIXME: We should emit a warning here using the diagnostic engine. + TSCBasic.stderrStream.write("warning: Usage of \(file) has been deprecated. Please delete it and use the new \(destination) instead.\n") + TSCBasic.stderrStream.flush() } } // in the case where ~/.swiftpm/configuration is the idiomatic location (eg on Linux) @@ -125,10 +127,12 @@ extension FileSystem { .filter{ self.isFile($0) && !self.isSymlink($0) && $0.extension != "lock"} for file in configurationFiles { let destination = idiomaticConfigurationDirectory.appending(component: file.basename) - observabilityScope?.emit(warning: "Usage of \(file) has been deprecated. Please delete it and use the new \(destination) instead.") if !self.exists(destination) { try self.copy(from: file, to: destination) } + // FIXME: We should emit a warning here using the diagnostic engine. + TSCBasic.stderrStream.write("warning: Usage of \(file) has been deprecated. Please delete it and use the new \(destination) instead.\n") + TSCBasic.stderrStream.flush() } } } diff --git a/Sources/Commands/APIDigester.swift b/Sources/Commands/APIDigester.swift index 921570a0574..c74f0192be7 100644 --- a/Sources/Commands/APIDigester.swift +++ b/Sources/Commands/APIDigester.swift @@ -99,13 +99,11 @@ struct APIDigesterBaselineDumper { try workingCopy.checkout(revision: baselineRevision) // Create the workspace for this package. - let workspace = try Workspace( - forRootPackage: baselinePackageRoot - ) + let workspace = try Workspace(forRootPackage: baselinePackageRoot) let graph = try workspace.loadPackageGraph( rootPath: baselinePackageRoot, - observabilityScope: observabilityScope + observabilityScope: self.observabilityScope ) // Don't emit a baseline for a module that didn't exist yet in this revision. diff --git a/Sources/Commands/SwiftPackageRegistryTool.swift b/Sources/Commands/SwiftPackageRegistryTool.swift index a81d5000f73..767c6dd1e7d 100644 --- a/Sources/Commands/SwiftPackageRegistryTool.swift +++ b/Sources/Commands/SwiftPackageRegistryTool.swift @@ -100,8 +100,8 @@ public struct SwiftPackageRegistryTool: ParsableCommand { } } - let configuration = try swiftTool.getRegistriesConfig() - if global { + let configuration = try getRegistriesConfig(swiftTool) + if self.global { try configuration.updateShared(with: set) } else { try configuration.updateLocal(with: set) @@ -141,12 +141,21 @@ public struct SwiftPackageRegistryTool: ParsableCommand { } } - let configuration = try swiftTool.getRegistriesConfig() - if global { + let configuration = try getRegistriesConfig(swiftTool) + if self.global { try configuration.updateShared(with: unset) } else { try configuration.updateLocal(with: unset) } } } + + static func getRegistriesConfig(_ swiftTool: SwiftTool) throws -> Workspace.Configuration.Registries { + let workspace = try swiftTool.getActiveWorkspace() + return try .init( + fileSystem: localFileSystem, + localRegistriesFile: workspace.location.localRegistriesConfigurationFile, + sharedRegistriesFile: workspace.location.sharedRegistriesConfigurationFile + ) + } } diff --git a/Sources/Commands/SwiftPackageTool.swift b/Sources/Commands/SwiftPackageTool.swift index 580e253f987..56024e63d53 100644 --- a/Sources/Commands/SwiftPackageTool.swift +++ b/Sources/Commands/SwiftPackageTool.swift @@ -1350,20 +1350,20 @@ extension SwiftPackageTool.Config { var mirrorURL: String func run(_ swiftTool: SwiftTool) throws { - let config = try swiftTool.getMirrorsConfig() + let config = try getMirrorsConfig(swiftTool) - if packageURL != nil { + if self.packageURL != nil { swiftTool.observabilityScope.emit( warning: "'--package-url' option is deprecated; use '--original-url' instead") } - guard let originalURL = packageURL ?? originalURL else { + guard let originalURL = self.packageURL ?? self.originalURL else { swiftTool.observabilityScope.emit(.missingRequiredArg("--original-url")) throw ExitCode.failure } try config.applyLocal { mirrors in - mirrors.set(mirrorURL: mirrorURL, forURL: originalURL) + mirrors.set(mirrorURL: self.mirrorURL, forURL: originalURL) } } } @@ -1385,14 +1385,14 @@ extension SwiftPackageTool.Config { var mirrorURL: String? func run(_ swiftTool: SwiftTool) throws { - let config = try swiftTool.getMirrorsConfig() + let config = try getMirrorsConfig(swiftTool) - if packageURL != nil { + if self.packageURL != nil { swiftTool.observabilityScope.emit( warning: "'--package-url' option is deprecated; use '--original-url' instead") } - guard let originalOrMirrorURL = packageURL ?? originalURL ?? mirrorURL else { + guard let originalOrMirrorURL = self.packageURL ?? self.originalURL ?? self.mirrorURL else { swiftTool.observabilityScope.emit(.missingRequiredArg("--original-url or --mirror-url")) throw ExitCode.failure } @@ -1417,14 +1417,14 @@ extension SwiftPackageTool.Config { var originalURL: String? func run(_ swiftTool: SwiftTool) throws { - let config = try swiftTool.getMirrorsConfig() + let config = try getMirrorsConfig(swiftTool) - if packageURL != nil { + if self.packageURL != nil { swiftTool.observabilityScope.emit( warning: "'--package-url' option is deprecated; use '--original-url' instead") } - guard let originalURL = packageURL ?? originalURL else { + guard let originalURL = self.packageURL ?? self.originalURL else { swiftTool.observabilityScope.emit(.missingRequiredArg("--original-url")) throw ExitCode.failure } @@ -1438,6 +1438,15 @@ extension SwiftPackageTool.Config { } } } + + static func getMirrorsConfig(_ swiftTool: SwiftTool) throws -> Workspace.Configuration.Mirrors { + let workspace = try swiftTool.getActiveWorkspace() + return try .init( + fileSystem: localFileSystem, + localMirrorsFile: workspace.location.localMirrorsConfigurationFile, + sharedMirrorsFile: workspace.location.sharedMirrorsConfigurationFile + ) + } } extension SwiftPackageTool { diff --git a/Sources/Commands/SwiftTool.swift b/Sources/Commands/SwiftTool.swift index 21f63a4b2b5..5d8d306b5ff 100644 --- a/Sources/Commands/SwiftTool.swift +++ b/Sources/Commands/SwiftTool.swift @@ -12,9 +12,7 @@ import ArgumentParser import Basics import Build import Dispatch -import func Foundation.NSUserName import class Foundation.ProcessInfo -import func Foundation.NSHomeDirectory import PackageGraph import PackageLoading import PackageModel @@ -489,7 +487,44 @@ public class SwiftTool { } - private func editsDirectory() throws -> AbsolutePath { + /// Returns the currently active workspace. + func getActiveWorkspace() throws -> Workspace { + if let workspace = _workspace { + return workspace + } + + let isXcodeBuildSystemEnabled = self.options.buildSystem == .xcode + let repositoryProvider = GitRepositoryProvider(processSet: self.processSet) + let delegate = ToolWorkspaceDelegate(self.outputStream, logLevel: self.logLevel, observabilityScope: self.observabilityScope) + let workspace = try Workspace( + fileSystem: localFileSystem, + location: .init( + workingDirectory: self.buildPath, + editsDirectory: self.getEditsDirectory(), + resolvedVersionsFile: self.getResolvedVersionsFile(), + localConfigurationDirectory: try self.getLocalConfigurationDirectory(), + sharedConfigurationDirectory: self.sharedConfigurationDirectory, + sharedSecurityDirectory: self.sharedSecurityDirectory, + sharedCacheDirectory: self.sharedCacheDirectory + ), + authorizationProvider: self.getAuthorizationProvider(), + configuration: .init( + skipDependenciesUpdates: options.skipDependencyUpdate, + prefetchBasedOnResolvedFile: options.shouldEnableResolverPrefetching, + additionalFileRules: isXcodeBuildSystemEnabled ? FileRuleDescription.xcbuildFileTypes : FileRuleDescription.swiftpmFileTypes, + sharedRepositoriesCacheEnabled: self.options.useRepositoriesCache, + fingerprintCheckingMode: self.options.resolverFingerprintCheckingMode + ), + customManifestLoader: self.getManifestLoader(), // FIXME: ideally we would not customize the manifest loader + customRepositoryProvider: repositoryProvider, // FIXME: ideally we would not customize the repository provider. its currently done for shutdown handling which can be better abstracted + delegate: delegate + ) + _workspace = workspace + _workspaceDelegate = delegate + return workspace + } + + private func getEditsDirectory() throws -> AbsolutePath { // TODO: replace multiroot-data-file with explicit overrides if let multiRootPackageDataFile = options.multirootPackageDataFile { return multiRootPackageDataFile.appending(component: "Packages") @@ -497,7 +532,7 @@ public class SwiftTool { return try Workspace.DefaultLocations.editsDirectory(forRootPackage: self.getPackageRoot()) } - private func resolvedVersionsFile() throws -> AbsolutePath { + private func getResolvedVersionsFile() throws -> AbsolutePath { // TODO: replace multiroot-data-file with explicit overrides if let multiRootPackageDataFile = options.multirootPackageDataFile { return multiRootPackageDataFile.appending(components: "xcshareddata", "swiftpm", "Package.resolved") @@ -505,34 +540,21 @@ public class SwiftTool { return try Workspace.DefaultLocations.resolvedVersionsFile(forRootPackage: self.getPackageRoot()) } - func getMirrorsConfig(sharedConfigurationDirectory: AbsolutePath? = nil) throws -> Workspace.Configuration.Mirrors { - let sharedConfigurationDirectory = sharedConfigurationDirectory ?? self.sharedConfigurationDirectory - let sharedMirrorFile = sharedConfigurationDirectory.map { Workspace.DefaultLocations.mirrorsConfigurationFile(at: $0) } - return try .init( - localMirrorFile: self.mirrorsConfigFile(), - sharedMirrorFile: sharedMirrorFile, - fileSystem: localFileSystem - ) - } - - private func mirrorsConfigFile() throws -> AbsolutePath { - // TODO: does this make sense now that we a global configuration as well? or should we at least rename it? - // Look for the override in the environment. - if let envPath = ProcessEnv.vars["SWIFTPM_MIRROR_CONFIG"] { - return try AbsolutePath(validating: envPath) - } - + internal func getLocalConfigurationDirectory() throws -> AbsolutePath { // Otherwise, use the default path. // TODO: replace multiroot-data-file with explicit overrides if let multiRootPackageDataFile = options.multirootPackageDataFile { // migrate from legacy location let legacyPath = multiRootPackageDataFile.appending(components: "xcshareddata", "swiftpm", "config") - let newPath = multiRootPackageDataFile.appending(components: "xcshareddata", "swiftpm", "configuration", "mirrors.json") + let newPath = Workspace.DefaultLocations.mirrorsConfigurationFile(at: multiRootPackageDataFile.appending(components: "xcshareddata", "swiftpm", "configuration")) if localFileSystem.exists(legacyPath) { - try localFileSystem.createDirectory(newPath.parentDirectory, recursive: true) - try localFileSystem.move(from: legacyPath, to: newPath) + observabilityScope.emit(warning: "Usage of \(legacyPath) has been deprecated. Please delete it and use the new \(newPath) instead.") + if !localFileSystem.exists(newPath) { + try localFileSystem.createDirectory(newPath.parentDirectory, recursive: true) + try localFileSystem.copy(from: legacyPath, to: newPath) + } } - return newPath + return newPath.parentDirectory } // migrate from legacy location @@ -545,22 +567,7 @@ public class SwiftTool { try localFileSystem.copy(from: legacyPath, to: newPath) } } - return newPath - } - - func getRegistriesConfig(sharedConfigurationDirectory: AbsolutePath? = nil) throws -> Workspace.Configuration.Registries { - let localRegistriesFile = try Workspace.DefaultLocations.registriesConfigurationFile(forRootPackage: self.getPackageRoot()) - - let sharedConfigurationDirectory = sharedConfigurationDirectory ?? self.sharedConfigurationDirectory - let sharedRegistriesFile = sharedConfigurationDirectory.map { - Workspace.DefaultLocations.registriesConfigurationFile(at: $0) - } - - return try .init( - localRegistriesFile: localRegistriesFile, - sharedRegistriesFile: sharedRegistriesFile, - fileSystem: localFileSystem - ) + return newPath.parentDirectory } func getAuthorizationProvider() throws -> AuthorizationProvider? { @@ -621,42 +628,6 @@ public class SwiftTool { return providers } - /// Returns the currently active workspace. - func getActiveWorkspace() throws -> Workspace { - if let workspace = _workspace { - return workspace - } - - let delegate = ToolWorkspaceDelegate(self.outputStream, logLevel: self.logLevel, observabilityScope: self.observabilityScope) - let provider = GitRepositoryProvider(processSet: processSet) - let isXcodeBuildSystemEnabled = self.options.buildSystem == .xcode - let workspace = try Workspace( - fileSystem: localFileSystem, - location: .init( - workingDirectory: self.buildPath, - editsDirectory: self.editsDirectory(), - resolvedVersionsFile: self.resolvedVersionsFile(), - sharedSecurityDirectory: self.sharedSecurityDirectory, - sharedCacheDirectory: self.sharedCacheDirectory, - sharedConfigurationDirectory: self.sharedConfigurationDirectory - ), - mirrors: self.getMirrorsConfig(sharedConfigurationDirectory: self.sharedConfigurationDirectory).mirrors, - registries: try self.getRegistriesConfig(sharedConfigurationDirectory: self.sharedConfigurationDirectory).configuration, - authorizationProvider: self.getAuthorizationProvider(), - customManifestLoader: self.getManifestLoader(), // FIXME: doe we really need to customize it? - customRepositoryProvider: provider, // FIXME: doe we really need to customize it? - additionalFileRules: isXcodeBuildSystemEnabled ? FileRuleDescription.xcbuildFileTypes : FileRuleDescription.swiftpmFileTypes, - resolverUpdateEnabled: !self.options.skipDependencyUpdate, - resolverPrefetchingEnabled: self.options.shouldEnableResolverPrefetching, - resolverFingerprintCheckingMode: self.options.resolverFingerprintCheckingMode, - sharedRepositoriesCacheEnabled: self.options.useRepositoriesCache, - delegate: delegate - ) - _workspace = workspace - _workspaceDelegate = delegate - return workspace - } - /// Start redirecting the standard output stream to the standard error stream. func redirectStdoutToStderr() { self.outputStream = TSCBasic.stderrStream @@ -1031,16 +1002,9 @@ private func getSharedSecurityDirectory(options: SwiftToolOptions, observability try localFileSystem.createDirectory(explicitSecurityPath, recursive: true) } return explicitSecurityPath - } - - do { - let sharedSecurityDirectory = try localFileSystem.getOrCreateSwiftPMSecurityDirectory() - // make sure we can write files - try withTemporaryFile(dir: sharedSecurityDirectory) { _ in } - return sharedSecurityDirectory - } catch { - observabilityScope.emit(warning: "Failed creating default security location, \(error)") - return .none + } else { + // further validation is done in workspace + return localFileSystem.swiftPMSecurityDirectory } } @@ -1051,16 +1015,9 @@ private func getSharedConfigurationDirectory(options: SwiftToolOptions, observab try localFileSystem.createDirectory(explicitConfigPath, recursive: true) } return explicitConfigPath - } - - do { - let sharedConfigurationDirector = try localFileSystem.getOrCreateSwiftPMConfigurationDirectory(observabilityScope: observabilityScope) - // make sure we can write files - try withTemporaryFile(dir: sharedConfigurationDirector) { _ in } - return sharedConfigurationDirector - } catch { - observabilityScope.emit(warning: "Failed creating default configuration location, \(error)") - return .none + } else { + // further validation is done in workspace + return localFileSystem.swiftPMConfigurationDirectory } } @@ -1071,16 +1028,9 @@ private func getSharedCacheDirectory(options: SwiftToolOptions, observabilitySco try localFileSystem.createDirectory(explicitCachePath, recursive: true) } return explicitCachePath - } - - do { - let sharedCacheDirector = try localFileSystem.getOrCreateSwiftPMCacheDirectory() - // make sure we can write files - try withTemporaryFile(dir: sharedCacheDirector) { _ in } - return sharedCacheDirector - } catch { - observabilityScope.emit(warning: "Failed creating default cache location, \(error)") - return .none + } else { + // further validation is done in workspace + return localFileSystem.swiftPMCacheDirectory } } diff --git a/Sources/PackageGraph/Pubgrub/PubgrubDependencyResolver.swift b/Sources/PackageGraph/Pubgrub/PubgrubDependencyResolver.swift index 056bc641787..c110a7259a1 100644 --- a/Sources/PackageGraph/Pubgrub/PubgrubDependencyResolver.swift +++ b/Sources/PackageGraph/Pubgrub/PubgrubDependencyResolver.swift @@ -106,10 +106,10 @@ public struct PubgrubDependencyResolver { private let packageContainerProvider: PackageContainerProvider /// Should resolver prefetch the containers. - private let prefetchingEnabled: Bool + private let prefetchBasedOnResolvedFile: Bool /// Update containers while fetching them. - private let updateEnabled: Bool + private let skipDependenciesUpdates: Bool /// Resolver delegate private let delegate: DependencyResolverDelegate? @@ -117,16 +117,21 @@ public struct PubgrubDependencyResolver { public init( provider: PackageContainerProvider, pinsMap: PinsStore.PinsMap = [:], - updateEnabled: Bool = true, - prefetchingEnabled: Bool = false, + skipDependenciesUpdates: Bool = false, + prefetchBasedOnResolvedFile: Bool = false, observabilityScope: ObservabilityScope, delegate: DependencyResolverDelegate? = nil ) { self.packageContainerProvider = provider self.pinsMap = pinsMap - self.updateEnabled = updateEnabled - self.prefetchingEnabled = prefetchingEnabled - self.provider = ContainerProvider(provider: self.packageContainerProvider, updateEnabled: self.updateEnabled, pinsMap: self.pinsMap, observabilityScope: observabilityScope) + self.skipDependenciesUpdates = skipDependenciesUpdates + self.prefetchBasedOnResolvedFile = prefetchBasedOnResolvedFile + self.provider = ContainerProvider( + provider: self.packageContainerProvider, + skipUpdate: self.skipDependenciesUpdates, + pinsMap: self.pinsMap, + observabilityScope: observabilityScope + ) self.delegate = delegate } @@ -171,7 +176,7 @@ public struct PubgrubDependencyResolver { let inputs = try self.processInputs(root: root, with: constraints) // Prefetch the containers if prefetching is enabled. - if self.prefetchingEnabled { + if self.prefetchBasedOnResolvedFile { // We avoid prefetching packages that are overridden since // otherwise we'll end up creating a repository container // for them. @@ -1370,7 +1375,7 @@ private final class ContainerProvider { private let underlying: PackageContainerProvider /// Whether to perform update (git fetch) on existing cloned repositories or not. - private let updateEnabled: Bool + private let skipUpdate: Bool /// Reference to the pins store. private let pinsMap: PinsStore.PinsMap @@ -1384,9 +1389,14 @@ private final class ContainerProvider { //// Store prefetches synchronization private var prefetches = ThreadSafeKeyValueStore() - init(provider underlying: PackageContainerProvider, updateEnabled: Bool, pinsMap: PinsStore.PinsMap, observabilityScope: ObservabilityScope) { + init( + provider underlying: PackageContainerProvider, + skipUpdate: Bool, + pinsMap: PinsStore.PinsMap, + observabilityScope: ObservabilityScope + ) { self.underlying = underlying - self.updateEnabled = updateEnabled + self.skipUpdate = skipUpdate self.pinsMap = pinsMap self.observabilityScope = observabilityScope } @@ -1420,7 +1430,12 @@ private final class ContainerProvider { } } else { // Otherwise, fetch the container from the provider - self.underlying.getContainer(for: package, skipUpdate: !self.updateEnabled, observabilityScope: self.observabilityScope, on: .sharedConcurrent) { result in + self.underlying.getContainer( + for: package, + skipUpdate: self.skipUpdate, + observabilityScope: self.observabilityScope, + on: .sharedConcurrent + ) { result in let result = result.tryMap { container -> PubGrubPackageContainer in let pubGrubContainer = PubGrubPackageContainer(underlying: container, pinsMap: self.pinsMap) // only cache positive results @@ -1444,7 +1459,12 @@ private final class ContainerProvider { return group } if needsFetching { - self.underlying.getContainer(for: identifier, skipUpdate: !self.updateEnabled, observabilityScope: self.observabilityScope, on: .sharedConcurrent) { result in + self.underlying.getContainer( + for: identifier, + skipUpdate: self.skipUpdate, + observabilityScope: self.observabilityScope, + on: .sharedConcurrent + ) { result in defer { self.prefetches[identifier]?.leave() } // only cache positive results if case .success(let container) = result { diff --git a/Sources/PackageRegistry/RegistryClient.swift b/Sources/PackageRegistry/RegistryClient.swift index 0f24fed0b71..ec223b8fb74 100644 --- a/Sources/PackageRegistry/RegistryClient.swift +++ b/Sources/PackageRegistry/RegistryClient.swift @@ -107,7 +107,7 @@ public final class RegistryClient { public init( configuration: RegistryConfiguration, identityResolver: IdentityResolver, - fingerprintStorage: PackageFingerprintStorage? = .none, + fingerprintStorage: PackageFingerprintStorage?, fingerprintCheckingMode: FingerprintCheckingMode, authorizationProvider: HTTPClientAuthorizationProvider? = .none, customHTTPClient: HTTPClient? = .none, diff --git a/Sources/SPMTestSupport/MockWorkspace.swift b/Sources/SPMTestSupport/MockWorkspace.swift index c020affeb83..64878150479 100644 --- a/Sources/SPMTestSupport/MockWorkspace.swift +++ b/Sources/SPMTestSupport/MockWorkspace.swift @@ -23,65 +23,60 @@ public typealias Diagnostic = TSCBasic.Diagnostic public final class MockWorkspace { let sandbox: AbsolutePath let fileSystem: InMemoryFileSystem - public let httpClient: HTTPClient - public var registryClient: RegistryClient - public let archiver: MockArchiver - public let checksumAlgorithm: MockHashAlgorithm - public let fingerprintStorage: MockPackageFingerprintStorage - public let customPackageContainerProvider: MockPackageContainerProvider? let roots: [MockPackage] let packages: [MockPackage] - public let mirrors: DependencyMirrors - let identityResolver: IdentityResolver - public var manifestLoader: MockManifestLoader - public var repositoryProvider: InMemoryGitRepositoryProvider + let toolsVersion: ToolsVersion + let fingerprints: MockPackageFingerprintStorage + let mirrors: DependencyMirrors + public var httpClient: HTTPClient + public var registryClient: RegistryClient let registry: MockRegistry + public var archiver: MockArchiver + public var checksumAlgorithm: MockHashAlgorithm + public private(set) var manifestLoader: MockManifestLoader + public let repositoryProvider: InMemoryGitRepositoryProvider + let identityResolver: IdentityResolver + let customPackageContainerProvider: MockPackageContainerProvider? public let delegate = MockWorkspaceDelegate() - let toolsVersion: ToolsVersion - let resolverUpdateEnabled: Bool + let skipDependenciesUpdates: Bool public init( sandbox: AbsolutePath, fileSystem: InMemoryFileSystem, - mirrors: DependencyMirrors? = nil, roots: [MockPackage], packages: [MockPackage], toolsVersion: ToolsVersion = ToolsVersion.currentToolsVersion, - customHttpClient: HTTPClient? = .none, - customRegistryClient: RegistryClient? = .none, - customBinaryArchiver: MockArchiver? = .none, - customChecksumAlgorithm: MockHashAlgorithm? = .none, - customFingerprintStorage: MockPackageFingerprintStorage? = .none, + fingerprints customFingerprints: MockPackageFingerprintStorage? = .none, + mirrors customMirrors: DependencyMirrors? = nil, + httpClient customHttpClient: HTTPClient? = .none, + registryClient customRegistryClient: RegistryClient? = .none, + binaryArchiver customBinaryArchiver: MockArchiver? = .none, + checksumAlgorithm customChecksumAlgorithm: MockHashAlgorithm? = .none, customPackageContainerProvider: MockPackageContainerProvider? = .none, - resolverUpdateEnabled: Bool = true + skipDependenciesUpdates: Bool = false ) throws { - let archiver = customBinaryArchiver ?? MockArchiver() - let httpClient = customHttpClient ?? HTTPClient.mock(fileSystem: fileSystem) - self.sandbox = sandbox self.fileSystem = fileSystem - self.httpClient = httpClient - self.archiver = archiver - self.checksumAlgorithm = customChecksumAlgorithm ?? MockHashAlgorithm() - self.fingerprintStorage = customFingerprintStorage ?? MockPackageFingerprintStorage() - self.mirrors = mirrors ?? DependencyMirrors() - self.identityResolver = DefaultIdentityResolver(locationMapper: self.mirrors.effectiveURL(for:)) - self.customPackageContainerProvider = customPackageContainerProvider self.roots = roots self.packages = packages - + self.fingerprints = customFingerprints ?? MockPackageFingerprintStorage() + self.mirrors = customMirrors ?? DependencyMirrors() + self.identityResolver = DefaultIdentityResolver(locationMapper: self.mirrors.effectiveURL(for:)) self.manifestLoader = MockManifestLoader(manifests: [:]) + self.customPackageContainerProvider = customPackageContainerProvider + self.checksumAlgorithm = customChecksumAlgorithm ?? MockHashAlgorithm() self.repositoryProvider = InMemoryGitRepositoryProvider() self.registry = MockRegistry( identityResolver: self.identityResolver, checksumAlgorithm: self.checksumAlgorithm, filesystem: self.fileSystem, - fingerprintStorage: self.fingerprintStorage + fingerprintStorage: self.fingerprints ) self.registryClient = customRegistryClient ?? self.registry.registryClient self.toolsVersion = toolsVersion - self.resolverUpdateEnabled = resolverUpdateEnabled - + self.skipDependenciesUpdates = skipDependenciesUpdates + self.httpClient = customHttpClient ?? HTTPClient.mock(fileSystem: fileSystem) + self.archiver = customBinaryArchiver ?? MockArchiver() try self.create() } @@ -233,17 +228,26 @@ public final class MockWorkspace { return workspace } - let workspace = try Workspace( + let workspace = try Workspace._init( fileSystem: self.fileSystem, location: .init( workingDirectory: self.sandbox.appending(component: ".build"), editsDirectory: self.sandbox.appending(component: "edits"), resolvedVersionsFile: self.sandbox.appending(component: "Package.resolved"), - sharedSecurityDirectory: self.fileSystem.swiftPMSecurityDirectory, - sharedCacheDirectory: self.fileSystem.swiftPMCacheDirectory, - sharedConfigurationDirectory: self.fileSystem.swiftPMConfigurationDirectory + localConfigurationDirectory: Workspace.DefaultLocations.configurationDirectory(forRootPackage: self.sandbox), + sharedConfigurationDirectory: self.fileSystem.swiftPMConfigurationDirectory, + sharedSecurityDirectory: self.fileSystem.swiftPMSecurityDirectory, + sharedCacheDirectory: self.fileSystem.swiftPMCacheDirectory + ), + configuration: .init( + skipDependenciesUpdates: self.skipDependenciesUpdates, + prefetchBasedOnResolvedFile: WorkspaceConfiguration.default.prefetchBasedOnResolvedFile, + additionalFileRules: WorkspaceConfiguration.default.additionalFileRules, + sharedRepositoriesCacheEnabled: WorkspaceConfiguration.default.sharedRepositoriesCacheEnabled, + fingerprintCheckingMode: .strict ), - mirrors: self.mirrors, + customFingerprints: self.fingerprints, + customMirrors: self.mirrors, customToolsVersion: self.toolsVersion, customManifestLoader: self.manifestLoader, customPackageContainerProvider: self.customPackageContainerProvider, @@ -253,10 +257,6 @@ public final class MockWorkspace { customHTTPClient: self.httpClient, customArchiver: self.archiver, customChecksumAlgorithm: self.checksumAlgorithm, - customFingerprintStorage: self.fingerprintStorage, - resolverUpdateEnabled: self.resolverUpdateEnabled, - resolverPrefetchingEnabled: true, - resolverFingerprintCheckingMode: .strict, delegate: self.delegate ) diff --git a/Sources/SPMTestSupport/misc.swift b/Sources/SPMTestSupport/misc.swift index 6ba85e1348a..77ec710c9a8 100644 --- a/Sources/SPMTestSupport/misc.swift +++ b/Sources/SPMTestSupport/misc.swift @@ -9,8 +9,6 @@ */ import Basics -import class Foundation.NSDate -import class Foundation.Thread import PackageGraph import PackageLoading import PackageModel diff --git a/Sources/SourceControl/RepositoryManager.swift b/Sources/SourceControl/RepositoryManager.swift index f4bfd070d45..5766c592bdd 100644 --- a/Sources/SourceControl/RepositoryManager.swift +++ b/Sources/SourceControl/RepositoryManager.swift @@ -82,14 +82,14 @@ public class RepositoryManager { fileSystem: FileSystem, path: AbsolutePath, provider: RepositoryProvider, - delegate: RepositoryManagerDelegate? = nil, - cachePath: AbsolutePath? = nil, - cacheLocalPackages: Bool? = nil + cachePath: AbsolutePath? = .none, + cacheLocalPackages: Bool = false, + delegate: RepositoryManagerDelegate? = .none ) { self.fileSystem = fileSystem self.path = path self.cachePath = cachePath - self.cacheLocalPackages = cacheLocalPackages ?? false + self.cacheLocalPackages = cacheLocalPackages self.provider = provider self.delegate = delegate @@ -108,7 +108,7 @@ public class RepositoryManager { self.repositories = [:] try? self.storage.reset() // FIXME: We should emit a warning here using the diagnostic engine. - TSCBasic.stderrStream.write("warning: unable to restore checkouts state: \(error)") + TSCBasic.stderrStream.write("warning: unable to restore checkouts state: \(error)\n") TSCBasic.stderrStream.flush() } } diff --git a/Sources/Workspace/Workspace.swift b/Sources/Workspace/Workspace.swift index 0dfdda2bcfa..ee710cba0fa 100644 --- a/Sources/Workspace/Workspace.swift +++ b/Sources/Workspace/Workspace.swift @@ -150,7 +150,6 @@ private struct WorkspaceDependencyResolverDelegate: DependencyResolverDelegate { func failedToResolve(incompatibility: Incompatibility) {} func solved(result: [(package: PackageReference, binding: BoundVersion, products: ProductFilter)]) {} } - /// A workspace represents the state of a working project directory. /// /// The workspace is responsible for managing the persistent working state of a @@ -201,7 +200,9 @@ public class Workspace { fileprivate let customPackageContainerProvider: PackageContainerProvider? /// The package container provider used by this workspace. - fileprivate var packageContainerProvider: PackageContainerProvider { return customPackageContainerProvider ?? self } + fileprivate var packageContainerProvider: PackageContainerProvider { + return self.customPackageContainerProvider ?? self + } /// The repository manager. // var for backwards compatibility with deprecated initializers, remove with them @@ -209,7 +210,7 @@ public class Workspace { /// The registry manager. // var for backwards compatibility with deprecated initializers, remove with them - fileprivate var registryClient: RegistryClient? + fileprivate var registryClient: RegistryClient /// The http client used for downloading binary artifacts. fileprivate let httpClient: HTTPClient @@ -222,19 +223,10 @@ public class Workspace { /// The algorithm used for generating file checksums. fileprivate let checksumAlgorithm: HashAlgorithm - /// The package fingerprint storage - fileprivate let fingerprintStorage: PackageFingerprintStorage? - - /// Enable prefetching containers in resolver. - fileprivate let resolverPrefetchingEnabled: Bool - - /// Update containers while fetching them. - fileprivate let resolverUpdateEnabled: Bool - - /// Fingerprint checking mode. - fileprivate let resolverFingerprintCheckingMode: FingerprintCheckingMode + /// The package fingerprints storage + fileprivate let fingerprints: PackageFingerprintStorage? - fileprivate let additionalFileRules: [FileRuleDescription] + fileprivate let configuration: WorkspaceConfiguration // state @@ -255,24 +247,136 @@ public class Workspace { /// - Parameters: /// - fileSystem: The file system to use. /// - location: Workspace location configuration. - /// - mirrors: Dependencies mirrors. - /// - authorizationProvider: Provider of authentication information. - /// - customToolsVersion: A custom tools version. - /// - customManifestLoader: A custom manifest loader. - /// - customRepositoryManager: A custom repository manager. - /// - customRepositoryProvider: A custom repository provider. - /// - customIdentityResolver: A custom identity resolver. - /// - customHTTPClient: A custom http client. - /// - customArchiver: A custom archiver. - /// - customChecksumAlgorithm: A custom checksum algorithm. - /// - customFingerprintStorage: A custom fingerprint storage. - /// - additionalFileRules: File rules to determine resource handling behavior. - /// - resolverUpdateEnabled: Enables the dependencies resolver automatic version update check. Enabled by default. When disabled the resolver relies only on the resolved version file - /// - resolverPrefetchingEnabled: Enables the dependencies resolver prefetching based on the resolved version file. Enabled by default. - /// - resolverFingerprintCheckingMode: Fingerprint checking mode. Defaults to `.warn`. - /// - sharedRepositoriesCacheEnabled: Enables the shared repository cache. Enabled by default. + /// - authorizationProvider: Provider of authentication information for outbound network requests. + /// - configuration: Configuration to fine tune the dependency resolution behavior. + /// - customManifestLoader: Custom manifest loader. Used to customize how manifest are loaded. + /// - customPackageContainerProvider: Custom package container provider. Used to provide specialized package providers. + /// - customRepositoryProvider: Custom repository provider. Used to customize source control access. /// - delegate: Delegate for workspace events - public init( + public convenience init( + fileSystem: FileSystem, + location: Location, + authorizationProvider: AuthorizationProvider? = .none, + configuration: WorkspaceConfiguration? = .none, + // optional customization used for advanced integration situations + customManifestLoader: ManifestLoaderProtocol? = .none, + customPackageContainerProvider: PackageContainerProvider? = .none, + customRepositoryProvider: RepositoryProvider? = .none, + // delegate + delegate: WorkspaceDelegate? = .none + ) throws { + try self.init( + fileSystem: fileSystem, + location: location, + authorizationProvider: authorizationProvider, + configuration: configuration, + customRegistriesConfiguration: .none, + customFingerprints: .none, + customMirrors: .none, + customToolsVersion: .none, + customManifestLoader: customManifestLoader, + customPackageContainerProvider: customPackageContainerProvider, + customRepositoryManager: .none, + customRepositoryProvider: customRepositoryProvider, + customRegistryClient: .none, + customIdentityResolver: .none, + customHTTPClient: .none, + customArchiver: .none, + customChecksumAlgorithm: .none, + delegate: delegate + ) + } + + /// A convenience method for creating a workspace for the given root + /// package path. + /// + /// The root package path is used to compute the build directory and other + /// default paths. + /// + /// - Parameters: + /// - fileSystem: The file system to use, defaults to local file system. + /// - forRootPackage: The path for the root package. + /// - authorizationProvider: Provider of authentication information for outbound network requests. + /// - configuration: Configuration to fine tune the dependency resolution behavior. + /// - customManifestLoader: Custom manifest loader. Used to customize how manifest are loaded. + /// - customPackageContainerProvider: Custom package container provider. Used to provide specialized package providers. + /// - customRepositoryProvider: Custom repository provider. Used to customize source control access. + /// - delegate: Delegate for workspace events + public convenience init( + fileSystem: FileSystem? = .none, + forRootPackage packagePath: AbsolutePath, + authorizationProvider: AuthorizationProvider? = .none, + configuration: WorkspaceConfiguration? = .none, + // optional customization used for advanced integration situations + customManifestLoader: ManifestLoaderProtocol? = .none, + customPackageContainerProvider: PackageContainerProvider? = .none, + customRepositoryProvider: RepositoryProvider? = .none, + // delegate + delegate: WorkspaceDelegate? = .none + ) throws { + let fileSystem = fileSystem ?? localFileSystem + let location = Location(forRootPackage: packagePath, fileSystem: fileSystem) + try self.init( + fileSystem: fileSystem, + location: location, + customManifestLoader: customManifestLoader, + customPackageContainerProvider: customPackageContainerProvider, + customRepositoryProvider: customRepositoryProvider, + delegate: delegate + ) + } + + /// A convenience method for creating a workspace for the given root + /// package path. + /// + /// The root package path is used to compute the build directory and other + /// default paths. + /// + /// - Parameters: + /// - fileSystem: The file system to use, defaults to local file system. + /// - forRootPackage: The path for the root package. + /// - authorizationProvider: Provider of authentication information for outbound network requests. + /// - configuration: Configuration to fine tune the dependency resolution behavior. + /// - customToolchain: Custom toolchain. Used to create a customized ManifestLoader, customizing how manifest are loaded. + /// - customPackageContainerProvider: Custom package container provider. Used to provide specialized package providers. + /// - customRepositoryProvider: Custom repository provider. Used to customize source control access. + /// - delegate: Delegate for workspace events + public convenience init( + fileSystem: FileSystem? = .none, + forRootPackage packagePath: AbsolutePath, + authorizationProvider: AuthorizationProvider? = .none, + configuration: WorkspaceConfiguration? = .none, + // optional customization used for advanced integration situations + customToolchain: UserToolchain, + customPackageContainerProvider: PackageContainerProvider? = .none, + customRepositoryProvider: RepositoryProvider? = .none, + // delegate + delegate: WorkspaceDelegate? = .none + ) throws { + let fileSystem = fileSystem ?? localFileSystem + let location = Location(forRootPackage: packagePath, fileSystem: fileSystem) + let manifestLoader = ManifestLoader( + toolchain: customToolchain.configuration, + cacheDir: location.sharedManifestsCacheDirectory + ) + try self.init( + fileSystem: fileSystem, + forRootPackage: packagePath, + authorizationProvider: authorizationProvider, + configuration: configuration, + customManifestLoader: manifestLoader, + customPackageContainerProvider: customPackageContainerProvider, + customRepositoryProvider: customRepositoryProvider, + delegate: delegate + ) + } + + + + // deprecate 12/21 + @_disfavoredOverload + @available(*, deprecated, message: "use alternative initializer") + public convenience init( fileSystem: FileSystem, location: Location, mirrors: DependencyMirrors? = .none, @@ -296,84 +400,34 @@ public class Workspace { sharedRepositoriesCacheEnabled: Bool? = .none, delegate: WorkspaceDelegate? = .none ) throws { - // defaults - let currentToolsVersion = customToolsVersion ?? ToolsVersion.currentToolsVersion - let toolsVersionLoader = ToolsVersionLoader(currentToolsVersion: currentToolsVersion) - let manifestLoader = try customManifestLoader ?? ManifestLoader( - toolchain: UserToolchain(destination: .hostDestination()).configuration, - cacheDir: location.sharedManifestsCacheDirectory + let configuration = WorkspaceConfiguration( + skipDependenciesUpdates: !(resolverUpdateEnabled ?? !WorkspaceConfiguration.default.skipDependenciesUpdates), + prefetchBasedOnResolvedFile: resolverPrefetchingEnabled ?? WorkspaceConfiguration.default.prefetchBasedOnResolvedFile, + additionalFileRules: additionalFileRules ?? WorkspaceConfiguration.default.additionalFileRules, + sharedRepositoriesCacheEnabled: sharedRepositoriesCacheEnabled ?? WorkspaceConfiguration.default.sharedRepositoriesCacheEnabled, + fingerprintCheckingMode: resolverFingerprintCheckingMode ) - let mirrors = mirrors ?? DependencyMirrors() - let identityResolver = customIdentityResolver ?? DefaultIdentityResolver(locationMapper: mirrors.effectiveURL(for:)) - let packageContainerProvider = customPackageContainerProvider - let repositoryProvider = customRepositoryProvider ?? GitRepositoryProvider() - let sharedRepositoriesCacheEnabled = sharedRepositoriesCacheEnabled ?? true - let repositoryManager = customRepositoryManager ?? RepositoryManager( - fileSystem: fileSystem, - path: location.repositoriesDirectory, - provider: repositoryProvider, - delegate: delegate.map(WorkspaceRepositoryManagerDelegate.init(workspaceDelegate:)), - cachePath: sharedRepositoriesCacheEnabled ? location.sharedRepositoriesCacheDirectory : .none - ) - let fingerprintStorage = customFingerprintStorage ?? location.sharedFingerprintsDirectory.map { - FilePackageFingerprintStorage( - fileSystem: fileSystem, - directoryPath: $0 - ) - } - let registryClient = customRegistryClient ?? registries.map { configuration in - RegistryClient( - configuration: configuration, - identityResolver: identityResolver, - fingerprintStorage: fingerprintStorage, - fingerprintCheckingMode: resolverFingerprintCheckingMode, - authorizationProvider: authorizationProvider?.httpAuthorizationHeader(for:) - ) - } - - // FIXME: use workspace scope when migrating workspace to new observability API - let httpClient = customHTTPClient ?? HTTPClient() - let archiver = customArchiver ?? ZipArchiver() - - let checksumAlgorithm = customChecksumAlgorithm ?? SHA256() - let additionalFileRules = additionalFileRules ?? [] - let resolverUpdateEnabled = resolverUpdateEnabled ?? true - let resolverPrefetchingEnabled = resolverPrefetchingEnabled ?? false - - // initialize - self.fileSystem = fileSystem - self.location = location - self.delegate = delegate - self.mirrors = mirrors - self.authorizationProvider = authorizationProvider - self.manifestLoader = manifestLoader - self.currentToolsVersion = currentToolsVersion - self.toolsVersionLoader = toolsVersionLoader - self.httpClient = httpClient - self.archiver = archiver - self.repositoryManager = repositoryManager - self.registryClient = registryClient - self.identityResolver = identityResolver - self.checksumAlgorithm = checksumAlgorithm - self.fingerprintStorage = fingerprintStorage - self.customPackageContainerProvider = packageContainerProvider - - self.pinsStore = LoadableResult { - try PinsStore( - pinsFile: location.resolvedVersionsFile, - workingDirectory: location.workingDirectory, - fileSystem: fileSystem, - mirrors: mirrors - ) - } - - self.additionalFileRules = additionalFileRules - self.resolverUpdateEnabled = resolverUpdateEnabled - self.resolverPrefetchingEnabled = resolverPrefetchingEnabled - self.resolverFingerprintCheckingMode = resolverFingerprintCheckingMode - - self.state = WorkspaceState(dataPath: self.location.workingDirectory, fileSystem: fileSystem) + try self.init( + fileSystem: fileSystem, + location: location, + authorizationProvider: authorizationProvider, + configuration: configuration, + customRegistriesConfiguration: registries, + customFingerprints: customFingerprintStorage, + customMirrors: mirrors, + customToolsVersion: customToolsVersion, + customManifestLoader: customManifestLoader, + customPackageContainerProvider: customPackageContainerProvider, + customRepositoryManager: customRepositoryManager, + customRepositoryProvider: customRepositoryProvider, + customRegistryClient: customRegistryClient, + customIdentityResolver: customIdentityResolver, + customHTTPClient: customHTTPClient, + customArchiver: customArchiver, + customChecksumAlgorithm: customChecksumAlgorithm, + delegate: delegate + ) } // deprecated 8/2021 @@ -411,9 +465,10 @@ public class Workspace { workingDirectory: dataPath, editsDirectory: editablesPath, resolvedVersionsFile: pinsFile, - sharedSecurityDirectory: fileSystem.swiftPMSecurityDirectory, - sharedCacheDirectory: cachePath, - sharedConfigurationDirectory: nil // legacy + localConfigurationDirectory: Workspace.DefaultLocations.configurationDirectory(forRootPackage: dataPath.parentDirectory), // legacy deprecated API + sharedConfigurationDirectory: .none, // legacy deprecated API + sharedSecurityDirectory: .none, // legacy deprecated API, + sharedCacheDirectory: cachePath ), mirrors: config?.mirrors, authorizationProvider: netrcFilePath.map { @@ -441,89 +496,186 @@ public class Workspace { /// /// The root package path is used to compute the build directory and other /// default paths. - /// - /// - Parameters: - /// - fileSystem: The file system to use, defaults to local file system. - /// - forRootPackage: The path for the root package. - /// - customToolchain: A custom toolchain. - /// - delegate: Delegate for workspace events - public convenience init( - fileSystem: FileSystem? = .none, + // deprecated 8/2021 + @available(*, deprecated, message: "use initializer instead") + public static func create( forRootPackage packagePath: AbsolutePath, - customToolchain: UserToolchain, - delegate: WorkspaceDelegate? = .none - ) throws { - let fileSystem = fileSystem ?? localFileSystem - let location = Location(forRootPackage: packagePath, fileSystem: fileSystem) - let manifestLoader = ManifestLoader( - toolchain: customToolchain.configuration, - cacheDir: location.sharedManifestsCacheDirectory - ) - try self.init( - fileSystem: fileSystem, + manifestLoader: ManifestLoaderProtocol, + repositoryManager: RepositoryManager? = nil, + delegate: WorkspaceDelegate? = nil, + identityResolver: IdentityResolver? = nil + ) -> Workspace { + let workspace = try! Workspace( forRootPackage: packagePath, customManifestLoader: manifestLoader, delegate: delegate ) + if let repositoryManager = repositoryManager { + workspace.repositoryManager = repositoryManager + } + if let identityResolver = identityResolver { + workspace.identityResolver = identityResolver + } + return workspace } - /// A convenience method for creating a workspace for the given root - /// package path. - /// - /// The root package path is used to compute the build directory and other - /// default paths. - /// - /// - Parameters: - /// - fileSystem: The file system to use, defaults to local file system. - /// - forRootPackage: The path for the root package. - /// - customManifestLoader: A custom manifest loader. - /// - delegate: Delegate for workspace events - public convenience init( - fileSystem: FileSystem? = .none, - forRootPackage packagePath: AbsolutePath, + /// Initializer for testing purposes only. Use non underscored initializers instead. + // this initializer is only public because of cross module visibility (eg MockWorkspace) + // as such it is by design an exact mirror of the private initializer below + public static func _init( + // core + fileSystem: FileSystem, + location: Location, + authorizationProvider: AuthorizationProvider? = .none, + configuration: WorkspaceConfiguration? = .none, + // optional customization, primarily designed for testing but also used in some cases by libSwiftPM consumers + customRegistriesConfiguration: RegistryConfiguration? = .none, + customFingerprints: PackageFingerprintStorage? = .none, + customMirrors: DependencyMirrors? = .none, + customToolsVersion: ToolsVersion? = .none, customManifestLoader: ManifestLoaderProtocol? = .none, + customPackageContainerProvider: PackageContainerProvider? = .none, + customRepositoryManager: RepositoryManager? = .none, + customRepositoryProvider: RepositoryProvider? = .none, + customRegistryClient: RegistryClient? = .none, + customIdentityResolver: IdentityResolver? = .none, + customHTTPClient: HTTPClient? = .none, + customArchiver: Archiver? = .none, + customChecksumAlgorithm: HashAlgorithm? = .none, + // delegate delegate: WorkspaceDelegate? = .none - ) throws { - let fileSystem = fileSystem ?? localFileSystem - let location = Location(forRootPackage: packagePath, fileSystem: fileSystem) - try self .init( + ) throws -> Workspace { + try .init( fileSystem: fileSystem, location: location, - mirrors: try Configuration.Mirrors( - forRootPackage: packagePath, - sharedMirrorFile: location.sharedMirrorsConfigurationFile, - fileSystem: fileSystem - ).mirrors, + authorizationProvider: authorizationProvider, + configuration: configuration, + customRegistriesConfiguration: customRegistriesConfiguration, + customFingerprints: customFingerprints, + customMirrors: customMirrors, + customToolsVersion: customToolsVersion, customManifestLoader: customManifestLoader, + customPackageContainerProvider: customPackageContainerProvider, + customRepositoryManager: customRepositoryManager, + customRepositoryProvider: customRepositoryProvider, + customRegistryClient: customRegistryClient, + customIdentityResolver: customIdentityResolver, + customHTTPClient: customHTTPClient, + customArchiver: customArchiver, + customChecksumAlgorithm: customChecksumAlgorithm, delegate: delegate ) } - /// A convenience method for creating a workspace for the given root - /// package path. - /// - /// The root package path is used to compute the build directory and other - /// default paths. - // deprecated 8/2021 - @available(*, deprecated, message: "use initializer instead") - public static func create( - forRootPackage packagePath: AbsolutePath, - manifestLoader: ManifestLoaderProtocol, - repositoryManager: RepositoryManager? = nil, - delegate: WorkspaceDelegate? = nil, - identityResolver: IdentityResolver? = nil - ) -> Workspace { - let workspace = try! Workspace(forRootPackage: packagePath, - customManifestLoader: manifestLoader, - delegate: delegate + private init( + // core + fileSystem: FileSystem, + location: Location, + authorizationProvider: AuthorizationProvider?, + configuration: WorkspaceConfiguration?, + // optional customization, primarily designed for testing but also used in some cases by libSwiftPM consumers + customRegistriesConfiguration: RegistryConfiguration?, + customFingerprints: PackageFingerprintStorage?, + customMirrors: DependencyMirrors?, + customToolsVersion: ToolsVersion?, + customManifestLoader: ManifestLoaderProtocol?, + customPackageContainerProvider: PackageContainerProvider?, + customRepositoryManager: RepositoryManager?, + customRepositoryProvider: RepositoryProvider?, + customRegistryClient: RegistryClient?, + customIdentityResolver: IdentityResolver?, + customHTTPClient: HTTPClient?, + customArchiver: Archiver?, + customChecksumAlgorithm: HashAlgorithm?, + // delegate + delegate: WorkspaceDelegate? + ) throws { + // we do not store the observabilityScope in the workspace initializer as the workspace is designed to be long lived. + // instead, observabilityScope is passed into the individual workspace methods which are short lived. + + // validate locations, returning a potentially modified one to deal with non-accessible or non-writable shared locations + let location = try location.validatingSharedLocations(fileSystem: fileSystem) + + let currentToolsVersion = customToolsVersion ?? ToolsVersion.currentToolsVersion + let toolsVersionLoader = ToolsVersionLoader(currentToolsVersion: currentToolsVersion) + let manifestLoader = try customManifestLoader ?? ManifestLoader( + toolchain: UserToolchain(destination: .hostDestination()).configuration, + cacheDir: location.sharedManifestsCacheDirectory ) - if let repositoryManager = repositoryManager { - workspace.repositoryManager = repositoryManager + + let configuration = configuration ?? .default + + let mirrors = try customMirrors ?? Workspace.Configuration.Mirrors( + fileSystem: fileSystem, + localMirrorsFile: location.localMirrorsConfigurationFile, + sharedMirrorsFile: location.sharedMirrorsConfigurationFile + ).mirrors + + let identityResolver = customIdentityResolver ?? DefaultIdentityResolver(locationMapper: mirrors.effectiveURL(for:)) + let repositoryProvider = customRepositoryProvider ?? GitRepositoryProvider() + let repositoryManager = customRepositoryManager ?? RepositoryManager( + fileSystem: fileSystem, + path: location.repositoriesDirectory, + provider: repositoryProvider, + cachePath: configuration.sharedRepositoriesCacheEnabled ? location.sharedRepositoriesCacheDirectory : .none, + delegate: delegate.map(WorkspaceRepositoryManagerDelegate.init(workspaceDelegate:)) + ) + + let fingerprints = customFingerprints ?? location.sharedFingerprintsDirectory.map { + FilePackageFingerprintStorage( + fileSystem: fileSystem, + directoryPath: $0 + ) } - if let identityResolver = identityResolver { - workspace.identityResolver = identityResolver + + let registriesConfiguration = try customRegistriesConfiguration ?? Workspace.Configuration.Registries( + fileSystem: fileSystem, + localRegistriesFile: location.localRegistriesConfigurationFile, + sharedRegistriesFile: location.sharedRegistriesConfigurationFile + ).configuration + + let registryClient = customRegistryClient ?? RegistryClient( + configuration: registriesConfiguration, + identityResolver: identityResolver, + fingerprintStorage: fingerprints, + fingerprintCheckingMode: configuration.fingerprintCheckingMode, + authorizationProvider: authorizationProvider?.httpAuthorizationHeader(for:) + ) + + let httpClient = customHTTPClient ?? HTTPClient() + let archiver = customArchiver ?? ZipArchiver() + let checksumAlgorithm = customChecksumAlgorithm ?? SHA256() + + // initialize + self.fileSystem = fileSystem + self.location = location + self.delegate = delegate + self.mirrors = mirrors + self.authorizationProvider = authorizationProvider + self.manifestLoader = manifestLoader + self.currentToolsVersion = currentToolsVersion + self.toolsVersionLoader = toolsVersionLoader + self.httpClient = httpClient + self.archiver = archiver + self.customPackageContainerProvider = customPackageContainerProvider + self.repositoryManager = repositoryManager + self.registryClient = registryClient + self.identityResolver = identityResolver + self.checksumAlgorithm = checksumAlgorithm + self.fingerprints = fingerprints + + self.pinsStore = LoadableResult { + try PinsStore( + pinsFile: location.resolvedVersionsFile, + workingDirectory: location.workingDirectory, + fileSystem: fileSystem, + mirrors: mirrors + ) } - return workspace + + self.configuration = configuration + + self.state = WorkspaceState(fileSystem: fileSystem, storageDirectory: self.location.workingDirectory) } } @@ -930,7 +1082,7 @@ extension Workspace { return try PackageGraph.load( root: manifests.root, identityResolver: self.identityResolver, - additionalFileRules: additionalFileRules, + additionalFileRules: self.configuration.additionalFileRules, externalManifests: manifests.allDependencyManifests(), requiredDependencies: manifests.computePackages().required, unsafeAllowedPackages: manifests.unsafeAllowedPackages(), @@ -1131,9 +1283,6 @@ extension Workspace { // MARK: - Editing Functions extension Workspace { - - - /// Edit implementation. fileprivate func _edit( packageName: String, @@ -3038,8 +3187,8 @@ extension Workspace { return PubgrubDependencyResolver( provider: packageContainerProvider, pinsMap: pinsMap, - updateEnabled: self.resolverUpdateEnabled, - prefetchingEnabled: self.resolverPrefetchingEnabled, + skipDependenciesUpdates: self.configuration.skipDependenciesUpdates, + prefetchBasedOnResolvedFile: self.configuration.prefetchBasedOnResolvedFile, observabilityScope: observabilityScope, delegate: delegate ) @@ -3169,8 +3318,8 @@ extension Workspace: PackageContainerProvider { manifestLoader: self.manifestLoader, toolsVersionLoader: self.toolsVersionLoader, currentToolsVersion: self.currentToolsVersion, - fingerprintStorage: self.fingerprintStorage, - fingerprintCheckingMode: self.resolverFingerprintCheckingMode, + fingerprintStorage: self.fingerprints, + fingerprintCheckingMode: self.configuration.fingerprintCheckingMode, observabilityScope: observabilityScope ) } @@ -3178,13 +3327,10 @@ extension Workspace: PackageContainerProvider { } // Resolve the container using the registry case .registry: - guard let registryClient = self.registryClient else { - throw StringError("registry not configured") - } let container = RegistryPackageContainer( package: package, identityResolver: self.identityResolver, - registryClient: registryClient, + registryClient: self.registryClient, manifestLoader: self.manifestLoader, toolsVersionLoader: self.toolsVersionLoader, currentToolsVersion: self.currentToolsVersion, @@ -3427,10 +3573,6 @@ extension Workspace { progressHandler: ((_ bytesReceived: Int64, _ totalBytes: Int64?) -> Void)? = .none, observabilityScope: ObservabilityScope ) throws -> AbsolutePath { - guard let registryClient = self.registryClient else { - throw StringError("registry not configured") - } - guard case (let scope, let name)? = package.identity.scopeAndName else { throw StringError("invalid package identity") } @@ -3441,7 +3583,7 @@ extension Workspace { } try temp_await { - registryClient.downloadSourceArchive( + self.registryClient.downloadSourceArchive( package: package.identity, version: version, fileSystem: self.fileSystem, @@ -3724,3 +3866,73 @@ extension Workspace.Location { self.editsDirectory.appending(dependency.subpath) } } + +extension Workspace.Location { + func validatingSharedLocations(fileSystem: FileSystem) throws -> Self { + var location = self + + // local configuration directory must be accessible, throw if we cannot access it + //try fileSystem.withLock(on: location.localConfigurationDirectory, type: .exclusive, {}) + + // check that shared configuration directory is accessible, or warn + reset if not + if let sharedConfigurationDirectory = self.sharedConfigurationDirectory { + // it may not always be possible to create default location (for example de to restricted sandbox) + let defaultDirectory = try? fileSystem.getOrCreateSwiftPMConfigurationDirectory() + if sharedConfigurationDirectory != defaultDirectory { + // custom location must be writable, throw if we cannot access it + try withTemporaryFile(dir: sharedConfigurationDirectory) { _ in } + } else { + do { + // default location may not be writable, in which case we disable the relevant features that depend on it + try withTemporaryFile(dir: sharedConfigurationDirectory) { _ in } + } catch { + location.sharedConfigurationDirectory = .none + // FIXME: We should emit a warning here using the diagnostic engine. + TSCBasic.stderrStream.write("warning: \(sharedConfigurationDirectory) is not accessible or not writable, disabling user-level configuration features. \(error)\n") + TSCBasic.stderrStream.flush() + } + } + } + + // check that shared configuration directory is accessible, or warn + reset if not + if let sharedSecurityDirectory = self.sharedSecurityDirectory { + // it may not always be possible to create default location (for example de to restricted sandbox) + let defaultDirectory = try? fileSystem.getOrCreateSwiftPMSecurityDirectory() + if sharedSecurityDirectory != defaultDirectory { + // custom location must be writable, throw if we cannot access it + try withTemporaryFile(dir: sharedSecurityDirectory) { _ in } + } else { + do { + // default location may not be writable, in which case we disable the relevant features that depend on it + try withTemporaryFile(dir: sharedSecurityDirectory) { _ in } + } catch { + location.sharedSecurityDirectory = .none + // FIXME: We should emit a warning here using the diagnostic engine. + TSCBasic.stderrStream.write("warning: \(sharedSecurityDirectory) is not accessible or not writable, disabling user-level security features. \(error)\n") + TSCBasic.stderrStream.flush() + } + } + } + + // check that shared configuration directory is accessible, or warn + reset if not + if let sharedCacheDirectory = self.sharedCacheDirectory { + // it may not always be possible to create default location (for example de to restricted sandbox) + let defaultDirectory = try? fileSystem.getOrCreateSwiftPMCacheDirectory() + if sharedCacheDirectory != defaultDirectory { + // custom location must be writable, throw if we cannot access it + try withTemporaryFile(dir: sharedCacheDirectory) { _ in } + } else { + do { + // default location may not be writable, in which case we disable the relevant features that depend on it + try withTemporaryFile(dir: sharedCacheDirectory) { _ in } + } catch { + location.sharedCacheDirectory = .none + // FIXME: We should emit a warning here using the diagnostic engine. + TSCBasic.stderrStream.write("warning: \(sharedCacheDirectory) is not accessible or not writable, disabling user-level cache features. \(error)\n") + TSCBasic.stderrStream.flush() + } + } + } + return location + } +} diff --git a/Sources/Workspace/WorkspaceConfiguration.swift b/Sources/Workspace/WorkspaceConfiguration.swift index e717268be9e..653a60d98ff 100644 --- a/Sources/Workspace/WorkspaceConfiguration.swift +++ b/Sources/Workspace/WorkspaceConfiguration.swift @@ -10,6 +10,7 @@ import Basics import Foundation +import PackageFingerprint import PackageGraph import PackageLoading import PackageModel @@ -30,15 +31,20 @@ extension Workspace { /// Path to the Package.resolved file. public var resolvedVersionsFile: AbsolutePath - + + /// Path to the local configuration directory + public var localConfigurationDirectory: AbsolutePath + + /// Path to the shared configuration directory + public var sharedConfigurationDirectory: AbsolutePath? + /// Path to the shared security directory public var sharedSecurityDirectory: AbsolutePath? /// Path to the shared cache directory public var sharedCacheDirectory: AbsolutePath? - /// Path to the shared configuration directory - public var sharedConfigurationDirectory: AbsolutePath? + // working directories /// Path to the repositories clones. public var repositoriesDirectory: AbsolutePath { @@ -59,20 +65,21 @@ extension Workspace { public var artifactsDirectory: AbsolutePath { self.workingDirectory.appending(component: "artifacts") } - - /// Path to the shared fingerprints directory. - public var sharedFingerprintsDirectory: AbsolutePath? { - self.sharedSecurityDirectory.map { $0.appending(component: "fingerprints") } - } - /// Path to the shared repositories cache. - public var sharedRepositoriesCacheDirectory: AbsolutePath? { - self.sharedCacheDirectory.map { $0.appending(component: "repositories") } + // Path to temporary files related to running plugins in the workspace + public var pluginWorkingDirectory: AbsolutePath { + self.workingDirectory.appending(component: "plugins") } - /// Path to the shared manifests cache. - public var sharedManifestsCacheDirectory: AbsolutePath? { - self.sharedCacheDirectory.map { DefaultLocations.manifestsDirectory(at: $0) } + // config locations + + /// Path to the local mirrors configuration. + public var localMirrorsConfigurationFile: AbsolutePath { + // backwards compatibility + if let customPath = ProcessEnv.vars["SWIFTPM_MIRROR_CONFIG"] { + return AbsolutePath(customPath) + } + return DefaultLocations.mirrorsConfigurationFile(at: self.localConfigurationDirectory) } /// Path to the shared mirrors configuration. @@ -80,16 +87,36 @@ extension Workspace { self.sharedConfigurationDirectory.map { DefaultLocations.mirrorsConfigurationFile(at: $0) } } + /// Path to the local registries configuration. + public var localRegistriesConfigurationFile: AbsolutePath { + DefaultLocations.registriesConfigurationFile(at: self.localConfigurationDirectory) + } + /// Path to the shared registries configuration. public var sharedRegistriesConfigurationFile: AbsolutePath? { self.sharedConfigurationDirectory.map { DefaultLocations.registriesConfigurationFile(at: $0) } } - - // Path to temporary files related to running plugins in the workspace - public var pluginWorkingDirectory: AbsolutePath { - self.workingDirectory.appending(component: "plugins") + + // security locations + + /// Path to the shared fingerprints directory. + public var sharedFingerprintsDirectory: AbsolutePath? { + self.sharedSecurityDirectory.map { $0.appending(component: "fingerprints") } + } + + // cache locations + + /// Path to the shared manifests cache. + public var sharedManifestsCacheDirectory: AbsolutePath? { + self.sharedCacheDirectory.map { DefaultLocations.manifestsDirectory(at: $0) } + } + + /// Path to the shared repositories cache. + public var sharedRepositoriesCacheDirectory: AbsolutePath? { + self.sharedCacheDirectory.map { $0.appending(component: "repositories") } } + /// Create a new workspace location. /// /// - Parameters: @@ -103,16 +130,18 @@ extension Workspace { workingDirectory: AbsolutePath, editsDirectory: AbsolutePath, resolvedVersionsFile: AbsolutePath, + localConfigurationDirectory: AbsolutePath, + sharedConfigurationDirectory: AbsolutePath?, sharedSecurityDirectory: AbsolutePath?, - sharedCacheDirectory: AbsolutePath?, - sharedConfigurationDirectory: AbsolutePath? + sharedCacheDirectory: AbsolutePath? ) { self.workingDirectory = workingDirectory self.editsDirectory = editsDirectory self.resolvedVersionsFile = resolvedVersionsFile + self.localConfigurationDirectory = localConfigurationDirectory + self.sharedConfigurationDirectory = sharedConfigurationDirectory self.sharedSecurityDirectory = sharedSecurityDirectory self.sharedCacheDirectory = sharedCacheDirectory - self.sharedConfigurationDirectory = sharedConfigurationDirectory } /// Create a new workspace location. @@ -124,9 +153,10 @@ extension Workspace { workingDirectory: DefaultLocations.workingDirectory(forRootPackage: rootPath), editsDirectory: DefaultLocations.editsDirectory(forRootPackage: rootPath), resolvedVersionsFile: DefaultLocations.resolvedVersionsFile(forRootPackage: rootPath), + localConfigurationDirectory: DefaultLocations.configurationDirectory(forRootPackage: rootPath), + sharedConfigurationDirectory: fileSystem.swiftPMConfigurationDirectory, sharedSecurityDirectory: fileSystem.swiftPMSecurityDirectory, - sharedCacheDirectory: fileSystem.swiftPMCacheDirectory, - sharedConfigurationDirectory: fileSystem.swiftPMConfigurationDirectory + sharedCacheDirectory: fileSystem.swiftPMCacheDirectory ) } } @@ -154,7 +184,7 @@ extension Workspace { } public static func mirrorsConfigurationFile(forRootPackage rootPath: AbsolutePath) -> AbsolutePath { - mirrorsConfigurationFile(at: configurationDirectory(forRootPackage: rootPath)) + mirrorsConfigurationFile(at: self.configurationDirectory(forRootPackage: rootPath)) } public static func mirrorsConfigurationFile(at path: AbsolutePath) -> AbsolutePath { @@ -162,7 +192,7 @@ extension Workspace { } public static func registriesConfigurationFile(forRootPackage rootPath: AbsolutePath) -> AbsolutePath { - registriesConfigurationFile(at: configurationDirectory(forRootPackage: rootPath)) + registriesConfigurationFile(at: self.configurationDirectory(forRootPackage: rootPath)) } public static func registriesConfigurationFile(at path: AbsolutePath) -> AbsolutePath { @@ -207,25 +237,31 @@ extension Workspace.Configuration { ) throws { let localMirrorConfigFile = Workspace.DefaultLocations.mirrorsConfigurationFile(forRootPackage: rootPath) try self.init( - localMirrorFile: localMirrorConfigFile, - sharedMirrorFile: sharedMirrorFile, - fileSystem: fileSystem + fileSystem: fileSystem, + localMirrorsFile: localMirrorConfigFile, + sharedMirrorsFile: sharedMirrorFile ) } + // deprecated 12/21 + @available(*, deprecated, message: "using init(fileSystem:localMirrorsFile:sharedMirrorsFile) instead") + public init(localMirrorFile: AbsolutePath, sharedMirrorFile: AbsolutePath?, fileSystem: FileSystem) throws { + try self.init(fileSystem: fileSystem, localMirrorsFile: localMirrorFile, sharedMirrorsFile: sharedMirrorFile) + } + /// Initialize the workspace mirrors configuration /// /// - Parameters: - /// - localMirrorFile: Path to the workspace mirrors configuration file - /// - sharedMirrorFile: Path to the shared mirrors configuration file, defaults to the standard location. /// - fileSystem: The file system to use. + /// - localMirrorsFile: Path to the workspace mirrors configuration file + /// - sharedMirrorsFile: Path to the shared mirrors configuration file, defaults to the standard location. public init( - localMirrorFile: AbsolutePath, - sharedMirrorFile: AbsolutePath?, - fileSystem: FileSystem + fileSystem: FileSystem, + localMirrorsFile: AbsolutePath, + sharedMirrorsFile: AbsolutePath? ) throws { - self.localMirrors = .init(path: localMirrorFile, fileSystem: fileSystem, deleteWhenEmpty: true) - self.sharedMirrors = sharedMirrorFile.map { .init(path: $0, fileSystem: fileSystem, deleteWhenEmpty: false) } + self.localMirrors = .init(path: localMirrorsFile, fileSystem: fileSystem, deleteWhenEmpty: true) + self.sharedMirrors = sharedMirrorsFile.map { .init(path: $0, fileSystem: fileSystem, deleteWhenEmpty: false) } self.fileSystem = fileSystem // computes the initial mirrors self._mirrors = DependencyMirrors() @@ -376,17 +412,17 @@ extension Workspace.Configuration { /// Initialize the workspace registries configuration /// /// - Parameters: + /// - fileSystem: The file system to use. /// - localRegistriesFile: Path to the workspace registries configuration file /// - sharedRegistriesFile: Path to the shared registries configuration file, defaults to the standard location. - /// - fileSystem: The file system to use. public init( + fileSystem: FileSystem, localRegistriesFile: AbsolutePath, - sharedRegistriesFile: AbsolutePath?, - fileSystem: FileSystem + sharedRegistriesFile: AbsolutePath? ) throws { + self.fileSystem = fileSystem self.localRegistries = .init(path: localRegistriesFile, fileSystem: fileSystem) self.sharedRegistries = sharedRegistriesFile.map { .init(path: $0, fileSystem: fileSystem) } - self.fileSystem = fileSystem try self.computeRegistries() } @@ -470,6 +506,51 @@ extension Workspace.Configuration { } } +// FIXME: better name +public struct WorkspaceConfiguration { + /// Enables the dependencies resolver automatic version updates. Disabled by default. + /// When disabled the resolver does not attempt to update the dependencies as part of resolution. + public var skipDependenciesUpdates: Bool + + /// Enables the dependencies resolver prefetching based on the resolved versions file. Enabled by default. + /// When disabled the resolver does not attempt to pre-fetch the dependencies based on the resolved versions file. + public var prefetchBasedOnResolvedFile: Bool + + /// File rules to determine resource handling behavior. + public var additionalFileRules: [FileRuleDescription] + + /// Enables the shared repository cache. Enabled by default. + public var sharedRepositoriesCacheEnabled: Bool + + /// Fingerprint checking mode. Defaults to warn. + public var fingerprintCheckingMode: FingerprintCheckingMode + + public init( + skipDependenciesUpdates: Bool, + prefetchBasedOnResolvedFile: Bool, + additionalFileRules: [FileRuleDescription], + sharedRepositoriesCacheEnabled: Bool, + fingerprintCheckingMode: FingerprintCheckingMode + ) { + self.skipDependenciesUpdates = skipDependenciesUpdates + self.prefetchBasedOnResolvedFile = prefetchBasedOnResolvedFile + self.additionalFileRules = additionalFileRules + self.sharedRepositoriesCacheEnabled = sharedRepositoriesCacheEnabled + self.fingerprintCheckingMode = fingerprintCheckingMode + } + + /// Default instance of WorkspaceConfiguration + public static var `default`: Self { + .init( + skipDependenciesUpdates: false, + prefetchBasedOnResolvedFile: true, + additionalFileRules: [], + sharedRepositoriesCacheEnabled: true, + fingerprintCheckingMode: .warn + ) + } +} + // MARK: - Deprecated 8/20201 extension Workspace { diff --git a/Sources/Workspace/WorkspaceState.swift b/Sources/Workspace/WorkspaceState.swift index 44102aa8b1a..1404b31a6bd 100644 --- a/Sources/Workspace/WorkspaceState.swift +++ b/Sources/Workspace/WorkspaceState.swift @@ -29,8 +29,8 @@ public final class WorkspaceState { /// storage private let storage: WorkspaceStateStorage - init(dataPath: AbsolutePath, fileSystem: FileSystem) { - self.storagePath = dataPath.appending(component: "workspace-state.json") + init(fileSystem: FileSystem, storageDirectory: AbsolutePath) { + self.storagePath = storageDirectory.appending(component: "workspace-state.json") self.storage = WorkspaceStateStorage(path: self.storagePath, fileSystem: fileSystem) // Load the state from disk, if possible. @@ -50,7 +50,7 @@ public final class WorkspaceState { self.artifacts = Workspace.ManagedArtifacts() try? self.storage.reset() // FIXME: We should emit a warning here using the diagnostic engine. - TSCBasic.stderrStream.write("warning: unable to restore workspace state: \(error)") + TSCBasic.stderrStream.write("warning: unable to restore workspace state: \(error)\n") TSCBasic.stderrStream.flush() } } diff --git a/Tests/FunctionalTests/MiscellaneousTests.swift b/Tests/FunctionalTests/MiscellaneousTests.swift index 06db5f73087..bf20e75e9d5 100644 --- a/Tests/FunctionalTests/MiscellaneousTests.swift +++ b/Tests/FunctionalTests/MiscellaneousTests.swift @@ -8,17 +8,14 @@ See http://swift.org/CONTRIBUTORS.txt for Swift project authors */ -import XCTest -import SPMTestSupport -import TSCBasic import PackageModel -import TSCUtility -import TSCLibc -import class Foundation.ProcessInfo -import class Foundation.Thread import SourceControl import SPMTestSupport +import TSCBasic +import TSCLibc +import TSCUtility import Workspace +import XCTest typealias ProcessID = TSCBasic.Process.ProcessID diff --git a/Tests/FunctionalTests/PluginTests.swift b/Tests/FunctionalTests/PluginTests.swift index 9d39318a079..821809b8429 100644 --- a/Tests/FunctionalTests/PluginTests.swift +++ b/Tests/FunctionalTests/PluginTests.swift @@ -331,7 +331,7 @@ class PluginTests: XCTestCase { let observability = ObservabilitySystem.makeForTesting() let workspace = try Workspace( fileSystem: localFileSystem, - location: .init(forRootPackage: packageDir, fileSystem: localFileSystem), + forRootPackage: packageDir, customManifestLoader: ManifestLoader(toolchain: ToolchainConfiguration.default), delegate: MockWorkspaceDelegate() ) diff --git a/Tests/SPMBuildCoreTests/PluginInvocationTests.swift b/Tests/SPMBuildCoreTests/PluginInvocationTests.swift index 5cce0b4b5b1..9b9f7279555 100644 --- a/Tests/SPMBuildCoreTests/PluginInvocationTests.swift +++ b/Tests/SPMBuildCoreTests/PluginInvocationTests.swift @@ -240,7 +240,7 @@ class PluginInvocationTests: XCTestCase { let observability = ObservabilitySystem.makeForTesting() let workspace = try Workspace( fileSystem: localFileSystem, - location: .init(forRootPackage: packageDir, fileSystem: localFileSystem), + forRootPackage: packageDir, customManifestLoader: ManifestLoader(toolchain: ToolchainConfiguration.default), delegate: MockWorkspaceDelegate() ) diff --git a/Tests/SourceControlTests/RepositoryManagerTests.swift b/Tests/SourceControlTests/RepositoryManagerTests.swift index b419486aff4..225c2bddcd0 100644 --- a/Tests/SourceControlTests/RepositoryManagerTests.swift +++ b/Tests/SourceControlTests/RepositoryManagerTests.swift @@ -8,12 +8,11 @@ See http://swift.org/CONTRIBUTORS.txt for Swift project authors */ -import XCTest - +import Basics +import SPMTestSupport @testable import SourceControl import TSCBasic - -import SPMTestSupport +import XCTest private enum DummyError: Swift.Error { case invalidRepository @@ -266,11 +265,17 @@ class RepositoryManagerTests: XCTestCase { try testWithTemporaryDirectory { path in let provider = DummyRepositoryProvider() let delegate = DummyRepositoryManagerDelegate() + delegate.willFetchGroup = DispatchGroup() delegate.didFetchGroup = DispatchGroup() delegate.willUpdateGroup = DispatchGroup() delegate.didUpdateGroup = DispatchGroup() - let manager = RepositoryManager(fileSystem: localFileSystem, path: path, provider: provider, delegate: delegate) + let manager = RepositoryManager( + fileSystem: localFileSystem, + path: path, + provider: provider, + delegate: delegate + ) // Check that we can "fetch" a repository. let dummyRepo = RepositorySpecifier(path: .init("/dummy")) @@ -394,9 +399,9 @@ class RepositoryManagerTests: XCTestCase { fileSystem: localFileSystem, path: repositoriesPath, provider: provider, - delegate: delegate, cachePath: cachePath, - cacheLocalPackages: true + cacheLocalPackages: true, + delegate: delegate ) // fetch packages and populate cache @@ -456,7 +461,12 @@ class RepositoryManagerTests: XCTestCase { delegate.didFetchGroup = DispatchGroup() try localFileSystem.createDirectory(repos, recursive: true) - let manager = RepositoryManager(fileSystem: localFileSystem, path: repos, provider: provider, delegate: delegate) + let manager = RepositoryManager( + fileSystem: localFileSystem, + path: repos, + provider: provider, + delegate: delegate + ) let dummyRepo = RepositorySpecifier(path: .init("/dummy")) delegate.willFetchGroup?.enter() @@ -489,7 +499,12 @@ class RepositoryManagerTests: XCTestCase { delegate.willFetchGroup = DispatchGroup() delegate.didFetchGroup = DispatchGroup() - let manager = RepositoryManager(fileSystem: localFileSystem, path: path, provider: provider, delegate: delegate) + let manager = RepositoryManager( + fileSystem: localFileSystem, + path: path, + provider: provider, + delegate: delegate + ) let dummyRepo = RepositorySpecifier(path: .init("/dummy")) delegate.willFetchGroup?.enter() @@ -509,7 +524,12 @@ class RepositoryManagerTests: XCTestCase { delegate.willFetchGroup = DispatchGroup() delegate.didFetchGroup = DispatchGroup() - let manager = RepositoryManager(fileSystem: localFileSystem, path: path, provider: provider, delegate: delegate) + let manager = RepositoryManager( + fileSystem: localFileSystem, + path: path, + provider: provider, + delegate: delegate + ) let dummyRepo = RepositorySpecifier(path: .init("/dummy")) _ = try manager.lookup(repository: dummyRepo) @@ -526,9 +546,19 @@ class RepositoryManagerTests: XCTestCase { delegate.willFetchGroup = DispatchGroup() delegate.didFetchGroup = DispatchGroup() - var manager = RepositoryManager(fileSystem: localFileSystem, path: path, provider: provider, delegate: delegate) + var manager = RepositoryManager( + fileSystem: localFileSystem, + path: path, + provider: provider, + delegate: delegate + ) try! localFileSystem.removeFileTree(path.appending(component: "checkouts-state.json")) - manager = RepositoryManager(fileSystem: localFileSystem, path: path, provider: provider, delegate: delegate) + manager = RepositoryManager( + fileSystem: localFileSystem, + path: path, + provider: provider, + delegate: delegate + ) let dummyRepo = RepositorySpecifier(path: .init("/dummy")) delegate.willFetchGroup?.enter() @@ -548,7 +578,12 @@ class RepositoryManagerTests: XCTestCase { try testWithTemporaryDirectory { path in let provider = DummyRepositoryProvider() let delegate = DummyRepositoryManagerDelegate() - let manager = RepositoryManager(fileSystem: localFileSystem, path: path, provider: provider, delegate: delegate) + let manager = RepositoryManager( + fileSystem: localFileSystem, + path: path, + provider: provider, + delegate: delegate + ) let dummyRepo = RepositorySpecifier(path: .init("/dummy")) // Condition to check if we have finished all lookups. let doneCondition = Condition() @@ -589,7 +624,12 @@ class RepositoryManagerTests: XCTestCase { try localFileSystem.createDirectory(repos, recursive: true) - let manager = RepositoryManager(fileSystem: localFileSystem, path: repos, provider: provider, delegate: delegate) + let manager = RepositoryManager( + fileSystem: localFileSystem, + path: repos, + provider: provider, + delegate: delegate + ) let dummyRepo = RepositorySpecifier(path: .init("/dummy")) delegate.willFetchGroup?.enter() @@ -629,7 +669,12 @@ class RepositoryManagerTests: XCTestCase { delegate.didFetchGroup = DispatchGroup() try localFileSystem.createDirectory(repos, recursive: true) - let manager = RepositoryManager(fileSystem: localFileSystem, path: repos, provider: provider, delegate: delegate) + let manager = RepositoryManager( + fileSystem: localFileSystem, + path: repos, + provider: provider, + delegate: delegate + ) let dummyRepo = RepositorySpecifier(path: .init("/dummy")) // Perform a lookup. diff --git a/Tests/WorkspaceTests/MirrorsConfigurationTests.swift b/Tests/WorkspaceTests/MirrorsConfigurationTests.swift index 464b883ffcb..b46edf17f99 100644 --- a/Tests/WorkspaceTests/MirrorsConfigurationTests.swift +++ b/Tests/WorkspaceTests/MirrorsConfigurationTests.swift @@ -108,9 +108,9 @@ final class MirrorsConfigurationTests: XCTestCase { let sharedConfigFile = AbsolutePath("/config/shared-mirrors.json") let config = try Workspace.Configuration.Mirrors( - localMirrorFile: localConfigFile, - sharedMirrorFile: sharedConfigFile, - fileSystem: fs + fileSystem: fs, + localMirrorsFile: localConfigFile, + sharedMirrorsFile: sharedConfigFile ) // first write to shared location diff --git a/Tests/WorkspaceTests/RegistryPackageContainerTests.swift b/Tests/WorkspaceTests/RegistryPackageContainerTests.swift index 53d3cb5cf57..109f815a5a2 100644 --- a/Tests/WorkspaceTests/RegistryPackageContainerTests.swift +++ b/Tests/WorkspaceTests/RegistryPackageContainerTests.swift @@ -82,7 +82,7 @@ class RegistryPackageContainerTests: XCTestCase { } ) - return try Workspace( + return try Workspace._init( fileSystem: fs, location: .init(forRootPackage: packagePath, fileSystem: fs), customToolsVersion: toolsVersion, @@ -147,7 +147,7 @@ class RegistryPackageContainerTests: XCTestCase { } ) - return try Workspace( + return try Workspace._init( fileSystem: fs, location: .init(forRootPackage: packagePath, fileSystem: fs), customToolsVersion: toolsVersion, @@ -233,7 +233,7 @@ class RegistryPackageContainerTests: XCTestCase { } ) - return try Workspace( + return try Workspace._init( fileSystem: fs, location: .init(forRootPackage: packagePath, fileSystem: fs), customToolsVersion: toolsVersion, diff --git a/Tests/WorkspaceTests/SourceControlPackageContainerTests.swift b/Tests/WorkspaceTests/SourceControlPackageContainerTests.swift index 59a671b6bbe..053f6fbbe46 100644 --- a/Tests/WorkspaceTests/SourceControlPackageContainerTests.swift +++ b/Tests/WorkspaceTests/SourceControlPackageContainerTests.swift @@ -199,7 +199,7 @@ class SourceControlPackageContainerTests: XCTestCase { delegate: MockResolverDelegate() ) - let provider = try Workspace( + let provider = try Workspace._init( fileSystem: fs, location: .init(forRootPackage: repoPath, fileSystem: fs), customManifestLoader: MockManifestLoader(manifests: [:]), @@ -252,7 +252,7 @@ class SourceControlPackageContainerTests: XCTestCase { ) func createProvider(_ currentToolsVersion: ToolsVersion) throws -> PackageContainerProvider { - return try Workspace( + return try Workspace._init( fileSystem: fs, location: .init(forRootPackage: repoPath, fileSystem: fs), customToolsVersion: currentToolsVersion, @@ -336,7 +336,7 @@ class SourceControlPackageContainerTests: XCTestCase { delegate: MockResolverDelegate() ) - let provider = try Workspace( + let provider = try Workspace._init( fileSystem: fs, location: .init(forRootPackage: repoPath, fileSystem: fs), customManifestLoader: MockManifestLoader(manifests: [:]), @@ -385,7 +385,7 @@ class SourceControlPackageContainerTests: XCTestCase { delegate: MockResolverDelegate() ) - let provider = try Workspace( + let provider = try Workspace._init( fileSystem: fs, location: .init(forRootPackage: repoPath, fileSystem: fs), customManifestLoader: MockManifestLoader(manifests: [:]), @@ -554,7 +554,12 @@ class SourceControlPackageContainerTests: XCTestCase { // Create a repository manager for it. let repoProvider = GitRepositoryProvider() - let repositoryManager = RepositoryManager(fileSystem: localFileSystem, path: packageDir, provider: repoProvider, delegate: nil) + let repositoryManager = RepositoryManager( + fileSystem: localFileSystem, + path: packageDir, + provider: repoProvider, + delegate: .none + ) // Create a container provider, configured with a mock manifest loader that will return the package manifest. let manifest = Manifest.createRootManifest( @@ -564,7 +569,7 @@ class SourceControlPackageContainerTests: XCTestCase { try TargetDescription(name: packageDir.basename, path: packageDir.pathString), ] ) - let containerProvider = try Workspace( + let containerProvider = try Workspace._init( fileSystem: localFileSystem, location: .init(forRootPackage: packageDir, fileSystem: localFileSystem), customManifestLoader: MockManifestLoader(manifests: [.init(url: packageDir.pathString, version: nil): manifest]), @@ -595,12 +600,11 @@ class SourceControlPackageContainerTests: XCTestCase { } } + // From rdar://problem/65284674 + // RepositoryPackageContainer used to erroneously cache dependencies based only on version, + // storing the result of the first product filter and then continually returning it for other filters too. + // This lead to corrupt graph states. func testRepositoryPackageContainerCache() throws { - // From rdar://problem/65284674 - // RepositoryPackageContainer used to erroneously cache dependencies based only on version, - // storing the result of the first product filter and then continually returning it for other filters too. - // This lead to corrupt graph states. - try testWithTemporaryDirectory { temporaryDirectory in let packageDirectory = temporaryDirectory.appending(component: "Package") try localFileSystem.createDirectory(packageDirectory) @@ -619,7 +623,7 @@ class SourceControlPackageContainerTests: XCTestCase { fileSystem: localFileSystem, path: packageDirectory, provider: repositoryProvider, - delegate: nil + delegate: .none ) let version = Version(1, 0, 0) @@ -642,7 +646,7 @@ class SourceControlPackageContainerTests: XCTestCase { ), ] ) - let containerProvider = try Workspace( + let containerProvider = try Workspace._init( fileSystem: localFileSystem, location: .init(forRootPackage: packageDirectory, fileSystem: localFileSystem), customManifestLoader: MockManifestLoader( diff --git a/Tests/WorkspaceTests/WorkspaceStateTests.swift b/Tests/WorkspaceTests/WorkspaceStateTests.swift index cd99e3bf0e9..2548b4c1dd9 100644 --- a/Tests/WorkspaceTests/WorkspaceStateTests.swift +++ b/Tests/WorkspaceTests/WorkspaceStateTests.swift @@ -8,8 +8,9 @@ See http://swift.org/CONTRIBUTORS.txt for Swift project authors */ -import TSCBasic +import Basics @testable import Workspace +import TSCBasic import XCTest final class WorkspaceStateTests: XCTestCase { @@ -17,71 +18,71 @@ final class WorkspaceStateTests: XCTestCase { let fs = InMemoryFileSystem() let buildDir = AbsolutePath("/.build") - let statePath = buildDir.appending(component: "workspace-state.json") + try fs.createDirectory(buildDir, recursive: true) + let statePath = buildDir.appending(component: "workspace-state.json") try fs.writeFileContents(statePath) { - $0 <<< - """ - { - "version": 4, - "object": { - "artifacts": [], - "dependencies": [ - { - "basedOn": null, - "packageRef": { - "identity": "yams", - "kind": "remote", - "location": "https://github.com/jpsim/Yams.git", - "name": "Yams" - }, - "state": { - "checkoutState": { - "revision": "9ff1cc9327586db4e0c8f46f064b6a82ec1566fa", - "version": "4.0.6" - }, - "name": "checkout" - }, - "subpath": "Yams" + """ + { + "version": 4, + "object": { + "artifacts": [], + "dependencies": [ + { + "basedOn": null, + "packageRef": { + "identity": "yams", + "kind": "remote", + "location": "https://github.com/jpsim/Yams.git", + "name": "Yams" + }, + "state": { + "checkoutState": { + "revision": "9ff1cc9327586db4e0c8f46f064b6a82ec1566fa", + "version": "4.0.6" + }, + "name": "checkout" + }, + "subpath": "Yams" + }, + { + "basedOn": null, + "packageRef": { + "identity": "swift-tools-support-core", + "kind": "remote", + "location": "https://github.com/apple/swift-tools-support-core.git", + "name": "swift-tools-support-core" + }, + "state": { + "checkoutState": { + "branch": "main", + "revision": "f9bbd6b80d67408021576adf6247e17c2e957d92", + "version": null + }, + "name": "checkout" }, - { - "basedOn": null, - "packageRef": { - "identity": "swift-tools-support-core", - "kind": "remote", - "location": "https://github.com/apple/swift-tools-support-core.git", - "name": "swift-tools-support-core" - }, - "state": { - "checkoutState": { - "branch": "main", - "revision": "f9bbd6b80d67408021576adf6247e17c2e957d92", - "version": null - }, - "name": "checkout" - }, - "subpath": "swift-tools-support-core" + "subpath": "swift-tools-support-core" + }, + { + "basedOn": null, + "packageRef": { + "identity": "swift-argument-parser", + "kind": "local", + "location": "/Users/tomerd/code/swift/swift-argument-parser", + "name": "swift-argument-parser" }, - { - "basedOn": null, - "packageRef": { - "identity": "swift-argument-parser", - "kind": "local", - "location": "/Users/tomerd/code/swift/swift-argument-parser", - "name": "swift-argument-parser" - }, - "state": { - "name": "local" - }, - "subpath": "swift-argument-parser" - } - ] - } + "state": { + "name": "local" + }, + "subpath": "swift-argument-parser" + } + ] } - """ + } + """ } - let state = WorkspaceState(dataPath: buildDir, fileSystem: fs) + 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") })) @@ -91,71 +92,71 @@ final class WorkspaceStateTests: XCTestCase { let fs = InMemoryFileSystem() let buildDir = AbsolutePath("/.build") - let statePath = buildDir.appending(component: "workspace-state.json") + try fs.createDirectory(buildDir, recursive: true) + let statePath = buildDir.appending(component: "workspace-state.json") try fs.writeFileContents(statePath) { - $0 <<< - """ - { - "version": 4, - "object": { - "artifacts": [], - "dependencies": [ - { - "basedOn": null, - "packageRef": { - "identity": "yams", - "kind": "remote", - "path": "https://github.com/jpsim/Yams.git", - "name": "Yams" - }, - "state": { - "checkoutState": { - "revision": "9ff1cc9327586db4e0c8f46f064b6a82ec1566fa", - "version": "4.0.6" - }, - "name": "checkout" - }, - "subpath": "Yams" + """ + { + "version": 4, + "object": { + "artifacts": [], + "dependencies": [ + { + "basedOn": null, + "packageRef": { + "identity": "yams", + "kind": "remote", + "path": "https://github.com/jpsim/Yams.git", + "name": "Yams" + }, + "state": { + "checkoutState": { + "revision": "9ff1cc9327586db4e0c8f46f064b6a82ec1566fa", + "version": "4.0.6" + }, + "name": "checkout" + }, + "subpath": "Yams" + }, + { + "basedOn": null, + "packageRef": { + "identity": "swift-tools-support-core", + "kind": "remote", + "path": "https://github.com/apple/swift-tools-support-core.git", + "name": "swift-tools-support-core" + }, + "state": { + "checkoutState": { + "branch": "main", + "revision": "f9bbd6b80d67408021576adf6247e17c2e957d92", + "version": null + }, + "name": "checkout" }, - { - "basedOn": null, - "packageRef": { - "identity": "swift-tools-support-core", - "kind": "remote", - "path": "https://github.com/apple/swift-tools-support-core.git", - "name": "swift-tools-support-core" - }, - "state": { - "checkoutState": { - "branch": "main", - "revision": "f9bbd6b80d67408021576adf6247e17c2e957d92", - "version": null - }, - "name": "checkout" - }, - "subpath": "swift-tools-support-core" + "subpath": "swift-tools-support-core" + }, + { + "basedOn": null, + "packageRef": { + "identity": "swift-argument-parser", + "kind": "local", + "path": "/Users/tomerd/code/swift/swift-argument-parser", + "name": "swift-argument-parser" }, - { - "basedOn": null, - "packageRef": { - "identity": "swift-argument-parser", - "kind": "local", - "path": "/Users/tomerd/code/swift/swift-argument-parser", - "name": "swift-argument-parser" - }, - "state": { - "name": "local" - }, - "subpath": "swift-argument-parser" - } - ] - } + "state": { + "name": "local" + }, + "subpath": "swift-argument-parser" + } + ] } - """ + } + """ } - let state = WorkspaceState(dataPath: buildDir, fileSystem: fs) + 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") })) @@ -165,71 +166,71 @@ final class WorkspaceStateTests: XCTestCase { let fs = InMemoryFileSystem() let buildDir = AbsolutePath("/.build") - let statePath = buildDir.appending(component: "workspace-state.json") + try fs.createDirectory(buildDir, recursive: true) + let statePath = buildDir.appending(component: "workspace-state.json") try fs.writeFileContents(statePath) { - $0 <<< - """ - { - "version": 5, - "object": { - "artifacts": [], - "dependencies": [ - { - "basedOn": null, - "packageRef": { - "identity": "yams", - "kind": "remoteSourceControl", - "location": "https://github.com/jpsim/Yams.git", - "name": "Yams" - }, - "state": { - "checkoutState": { - "revision": "9ff1cc9327586db4e0c8f46f064b6a82ec1566fa", - "version": "4.0.6" - }, - "name": "checkout" - }, - "subpath": "Yams" + """ + { + "version": 5, + "object": { + "artifacts": [], + "dependencies": [ + { + "basedOn": null, + "packageRef": { + "identity": "yams", + "kind": "remoteSourceControl", + "location": "https://github.com/jpsim/Yams.git", + "name": "Yams" + }, + "state": { + "checkoutState": { + "revision": "9ff1cc9327586db4e0c8f46f064b6a82ec1566fa", + "version": "4.0.6" + }, + "name": "checkout" + }, + "subpath": "Yams" + }, + { + "basedOn": null, + "packageRef": { + "identity": "swift-tools-support-core", + "kind": "remoteSourceControl", + "location": "https://github.com/apple/swift-tools-support-core.git", + "name": "swift-tools-support-core" + }, + "state": { + "checkoutState": { + "branch": "main", + "revision": "f9bbd6b80d67408021576adf6247e17c2e957d92" + }, + "name": "checkout" }, - { - "basedOn": null, - "packageRef": { - "identity": "swift-tools-support-core", - "kind": "remoteSourceControl", - "location": "https://github.com/apple/swift-tools-support-core.git", - "name": "swift-tools-support-core" - }, - "state": { - "checkoutState": { - "branch": "main", - "revision": "f9bbd6b80d67408021576adf6247e17c2e957d92" - }, - "name": "checkout" - }, - "subpath": "swift-tools-support-core" + "subpath": "swift-tools-support-core" + }, + { + "basedOn": null, + "packageRef": { + "identity": "swift-argument-parser", + "kind": "fileSystem", + "location": "/Users/tomerd/code/swift/swift-argument-parser", + "name": "swift-argument-parser" }, - { - "basedOn": null, - "packageRef": { - "identity": "swift-argument-parser", - "kind": "fileSystem", - "location": "/Users/tomerd/code/swift/swift-argument-parser", - "name": "swift-argument-parser" - }, - "state": { - "name": "local", - "path": "/Users/tomerd/code/swift/swift-argument-parser" - }, - "subpath": "swift-argument-parser" - } - ] - } + "state": { + "name": "local", + "path": "/Users/tomerd/code/swift/swift-argument-parser" + }, + "subpath": "swift-argument-parser" + } + ] } - """ + } + """ } - let state = WorkspaceState(dataPath: buildDir, fileSystem: fs) + 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") })) @@ -239,57 +240,57 @@ final class WorkspaceStateTests: XCTestCase { let fs = InMemoryFileSystem() let buildDir = AbsolutePath("/.build") - let statePath = buildDir.appending(component: "workspace-state.json") + try fs.createDirectory(buildDir, recursive: true) + let statePath = buildDir.appending(component: "workspace-state.json") try fs.writeFileContents(statePath) { - $0 <<< - """ - { - "version": 5, - "object": { - "artifacts": [], - "dependencies": [ - { - "basedOn": null, - "packageRef": { - "identity": "yams", - "kind": "remoteSourceControl", - "location": "https://github.com/jpsim/Yams.git", - "name": "Yams" - }, - "state": { - "checkoutState": { - "revision": "9ff1cc9327586db4e0c8f46f064b6a82ec1566fa", - "version": "4.0.6" - }, - "name": "checkout" - }, - "subpath": "Yams" + """ + { + "version": 5, + "object": { + "artifacts": [], + "dependencies": [ + { + "basedOn": null, + "packageRef": { + "identity": "yams", + "kind": "remoteSourceControl", + "location": "https://github.com/jpsim/Yams.git", + "name": "Yams" + }, + "state": { + "checkoutState": { + "revision": "9ff1cc9327586db4e0c8f46f064b6a82ec1566fa", + "version": "4.0.6" + }, + "name": "checkout" + }, + "subpath": "Yams" + }, + { + "basedOn": null, + "packageRef": { + "identity": "swift-argument-parser", + "kind": "remoteSourceControl", + "location": "https://github.com/apple/swift-argument-parser.git", + "name": "swift-argument-parser" + }, + "state": { + "checkoutState": { + "revision": "83b23d940471b313427da226196661856f6ba3e0", + "version": "0.4.4" + }, + "name": "checkout" }, - { - "basedOn": null, - "packageRef": { - "identity": "swift-argument-parser", - "kind": "remoteSourceControl", - "location": "https://github.com/apple/swift-argument-parser.git", - "name": "swift-argument-parser" - }, - "state": { - "checkoutState": { - "revision": "83b23d940471b313427da226196661856f6ba3e0", - "version": "0.4.4" - }, - "name": "checkout" - }, - "subpath": "swift-argument-parser" - } - ] - } + "subpath": "swift-argument-parser" + } + ] } - """ + } + """ } - let state = WorkspaceState(dataPath: buildDir, fileSystem: fs) + let state = WorkspaceState(fileSystem: fs, storageDirectory: buildDir) try state.save() let serialized = try fs.readFileContents(statePath).description diff --git a/Tests/WorkspaceTests/WorkspaceTests.swift b/Tests/WorkspaceTests/WorkspaceTests.swift index a5b909e95d1..dff5375daf4 100644 --- a/Tests/WorkspaceTests/WorkspaceTests.swift +++ b/Tests/WorkspaceTests/WorkspaceTests.swift @@ -125,6 +125,7 @@ final class WorkspaceTests: XCTestCase { func testInterpreterFlags() throws { let fs = localFileSystem + try testWithTemporaryDirectory { path in let foo = path.appending(component: "foo") @@ -138,7 +139,7 @@ final class WorkspaceTests: XCTestCase { let sandbox = path.appending(component: "ws") return try Workspace( fileSystem: fs, - location: .init(forRootPackage: sandbox, fileSystem: fs), + forRootPackage: sandbox, customManifestLoader: manifestLoader, delegate: MockWorkspaceDelegate() ) @@ -178,6 +179,7 @@ final class WorkspaceTests: XCTestCase { func testManifestParseError() throws { let observability = ObservabilitySystem.makeForTesting() + try testWithTemporaryDirectory { path in let pkgDir = path.appending(component: "MyPkg") try localFileSystem.writeFileContents(pkgDir.appending(component: "Package.swift")) { @@ -193,7 +195,7 @@ final class WorkspaceTests: XCTestCase { } let workspace = try Workspace( fileSystem: localFileSystem, - location: .init(forRootPackage: pkgDir, fileSystem: localFileSystem), + forRootPackage: pkgDir, customManifestLoader: ManifestLoader(toolchain: ToolchainConfiguration.default), delegate: MockWorkspaceDelegate() ) @@ -2776,7 +2778,7 @@ final class WorkspaceTests: XCTestCase { versions: ["1.5.0"] ), ], - resolverUpdateEnabled: false + skipDependenciesUpdates: true ) // Run update and remove all events. @@ -3518,7 +3520,6 @@ final class WorkspaceTests: XCTestCase { let workspace = try MockWorkspace( sandbox: sandbox, fileSystem: fs, - mirrors: mirrors, roots: [ MockPackage( name: "Foo", @@ -3579,7 +3580,8 @@ final class WorkspaceTests: XCTestCase { ], versions: ["1.0.0", "1.5.0"] ), - ] + ], + mirrors: mirrors ) let deps: [MockDependency] = [ @@ -3960,7 +3962,10 @@ final class WorkspaceTests: XCTestCase { // Load the workspace. let observability = ObservabilitySystem.makeForTesting() - let workspace = try Workspace(forRootPackage: packagePath, customToolchain: UserToolchain.default) + let workspace = try Workspace( + forRootPackage: packagePath, + customToolchain: UserToolchain.default + ) // From here the API should be simple and straightforward: let manifest = try tsc_await { @@ -4455,7 +4460,7 @@ final class WorkspaceTests: XCTestCase { versions: ["1.0.0"] ) ], - customBinaryArchiver: archiver + binaryArchiver: archiver ) // Create dummy xcframework/artifactbundle zip files @@ -4665,8 +4670,8 @@ final class WorkspaceTests: XCTestCase { versions: ["1.0.0"] ) ], - customHttpClient: httpClient, - customBinaryArchiver: archiver + httpClient: httpClient, + binaryArchiver: archiver ) // Create dummy xcframework directories and zip files @@ -4838,7 +4843,7 @@ final class WorkspaceTests: XCTestCase { versions: ["1.0.0"] ), ], - customBinaryArchiver: archiver + binaryArchiver: archiver ) // Create dummy zip files @@ -4898,7 +4903,7 @@ final class WorkspaceTests: XCTestCase { versions: ["1.0.0"] ), ], - customBinaryArchiver: archiver + binaryArchiver: archiver ) workspace.checkPackageGraphFailure(roots: ["Foo"]) { diagnostics in @@ -4973,7 +4978,7 @@ final class WorkspaceTests: XCTestCase { versions: ["1.0.0"] ) ], - customBinaryArchiver: archiver + binaryArchiver: archiver ) // Create dummy zip files @@ -5059,7 +5064,7 @@ final class WorkspaceTests: XCTestCase { versions: ["1.0.0"] ) ], - customBinaryArchiver: archiver + binaryArchiver: archiver ) // Pin A to 1.0.0, Checkout B to 1.0.0 @@ -5295,8 +5300,8 @@ final class WorkspaceTests: XCTestCase { versions: ["1.0.0"] ), ], - customHttpClient: httpClient, - customBinaryArchiver: archiver + httpClient: httpClient, + binaryArchiver: archiver ) try workspace.checkPackageGraph(roots: ["Foo"]) { graph, diagnostics in @@ -5501,8 +5506,8 @@ final class WorkspaceTests: XCTestCase { versions: ["1.0.0"] ), ], - customHttpClient: httpClient, - customBinaryArchiver: archiver + httpClient: httpClient, + binaryArchiver: archiver ) let a4FrameworkPath = workspace.packagesDir.appending(components: "A", "XCFrameworks", "A4.xcframework") @@ -5736,8 +5741,8 @@ final class WorkspaceTests: XCTestCase { versions: ["1.0.0"] ) ], - customHttpClient: httpClient, - customBinaryArchiver: archiver + httpClient: httpClient, + binaryArchiver: archiver ) try workspace.checkPackageGraph(roots: ["Foo"]) { graph, diagnostics in @@ -5870,8 +5875,8 @@ final class WorkspaceTests: XCTestCase { versions: ["1.0.0"] ), ], - customHttpClient: httpClient, - customBinaryArchiver: archiver + httpClient: httpClient, + binaryArchiver: archiver ) workspace.checkPackageGraphFailure(roots: ["Foo"]) { diagnostics in @@ -5919,7 +5924,7 @@ final class WorkspaceTests: XCTestCase { versions: ["0.9.0", "1.0.0"] ), ], - customHttpClient: httpClient + httpClient: httpClient ) // Pin A to 1.0.0, Checkout A to 1.0.0 @@ -6040,8 +6045,8 @@ final class WorkspaceTests: XCTestCase { versions: ["1.0.0"] ) ], - customHttpClient: httpClient, - customBinaryArchiver: archiver + httpClient: httpClient, + binaryArchiver: archiver ) try workspace.checkPackageGraph(roots: ["Foo"]) { graph, diagnostics in @@ -6189,8 +6194,8 @@ final class WorkspaceTests: XCTestCase { versions: ["1.0.0"] ) ], - customHttpClient: httpClient, - customBinaryArchiver: archiver + httpClient: httpClient, + binaryArchiver: archiver ) try workspace.checkPackageGraph(roots: ["Foo"]) { graph, diagnostics in @@ -6387,9 +6392,9 @@ final class WorkspaceTests: XCTestCase { versions: ["1.0.0"] ), ], - customHttpClient: httpClient, - customBinaryArchiver: archiver, - customChecksumAlgorithm: checksumAlgorithm + httpClient: httpClient, + binaryArchiver: archiver, + checksumAlgorithm: checksumAlgorithm ) try workspace.checkPackageGraph(roots: ["Foo"]) { graph, diagnostics in @@ -6487,7 +6492,7 @@ final class WorkspaceTests: XCTestCase { versions: ["0.9.0", "1.0.0"] ), ], - customHttpClient: httpClient + httpClient: httpClient ) workspace.checkPackageGraphFailure(roots: ["Foo"]) { diagnostics in @@ -6560,7 +6565,7 @@ final class WorkspaceTests: XCTestCase { versions: ["0.9.0", "1.0.0"] ), ], - customHttpClient: httpClient + httpClient: httpClient ) workspace.checkPackageGraphFailure(roots: ["Foo"]) { diagnostics in @@ -6739,8 +6744,8 @@ final class WorkspaceTests: XCTestCase { versions: ["0.9.0", "1.0.0"] ), ], - customHttpClient: httpClient, - customBinaryArchiver: archiver + httpClient: httpClient, + binaryArchiver: archiver ) workspace.checkPackageGraphFailure(roots: ["Foo"]) { diagnostics in @@ -6818,7 +6823,7 @@ final class WorkspaceTests: XCTestCase { versions: ["0.9.0", "1.0.0"] ), ], - customHttpClient: httpClient + httpClient: httpClient ) workspace.checkPackageGraphFailure(roots: ["Foo"]) { diagnostics in @@ -6894,7 +6899,7 @@ final class WorkspaceTests: XCTestCase { versions: ["0.9.0", "1.0.0"] ), ], - customHttpClient: httpClient + httpClient: httpClient ) workspace.checkPackageGraphFailure(roots: ["Foo"]) { diagnostics in @@ -8295,7 +8300,7 @@ final class WorkspaceTests: XCTestCase { let sandbox = path.appending(component: "ws") let workspace = try Workspace( fileSystem: fs, - location: .init(forRootPackage: sandbox, fileSystem: fs), + forRootPackage: sandbox, customManifestLoader: manifestLoader, delegate: MockWorkspaceDelegate() ) @@ -8360,7 +8365,7 @@ final class WorkspaceTests: XCTestCase { let delegate = MockWorkspaceDelegate() let workspace = try Workspace( fileSystem: fs, - location: .init(forRootPackage: .root, fileSystem: fs), + forRootPackage: .root, customManifestLoader: TestLoader(error: .none), delegate: delegate ) @@ -8375,7 +8380,7 @@ final class WorkspaceTests: XCTestCase { let delegate = MockWorkspaceDelegate() let workspace = try Workspace( fileSystem: fs, - location: .init(forRootPackage: .root, fileSystem: fs), + forRootPackage: .root, customManifestLoader: TestLoader(error: Diagnostics.fatalError), delegate: delegate ) @@ -8390,7 +8395,7 @@ final class WorkspaceTests: XCTestCase { let delegate = MockWorkspaceDelegate() let workspace = try Workspace( fileSystem: fs, - location: .init(forRootPackage: .root, fileSystem: fs), + forRootPackage: .root, customManifestLoader: TestLoader(error: StringError("boom")), delegate: delegate ) @@ -9018,7 +9023,7 @@ final class WorkspaceTests: XCTestCase { versions: ["1.0.0"] ) ], - customRegistryClient: registryClient + registryClient: registryClient ) workspace.checkPackageGraphFailure(roots: ["MyPackage"]) { diagnostics in