Skip to content

Commit

Permalink
refactor workspace initializers
Browse files Browse the repository at this point in the history
motivation: workspace initializer is very robust, accomodating the needs of both libSwiftPM consumer and testing. we want to seperate these concerns a bit so that libSwiftPM exposes the minimum API surface area

changes:
* deprecate existing workspace inisitlazer
* create new minimalistic public initializer oriented towards libSwiftPM consumers
* create internal initializer with all custmization options oriented towards testing
* better abstract resolver configuraiton and other behavior settings into a struct
* pass observability scope to workspace initializer via thread local (from CLI)
* update callsite and tests to the new API
  • Loading branch information
tomerd committed Dec 23, 2021
1 parent af43c15 commit 4757018
Show file tree
Hide file tree
Showing 22 changed files with 1,061 additions and 728 deletions.
10 changes: 7 additions & 3 deletions Sources/Basics/FileSystem+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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()
}
}
}
Expand Down
6 changes: 2 additions & 4 deletions Sources/Commands/APIDigester.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
17 changes: 13 additions & 4 deletions Sources/Commands/SwiftPackageRegistryTool.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
)
}
}
29 changes: 19 additions & 10 deletions Sources/Commands/SwiftPackageTool.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}
Expand All @@ -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
}
Expand All @@ -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
}
Expand All @@ -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 {
Expand Down
164 changes: 57 additions & 107 deletions Sources/Commands/SwiftTool.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -489,50 +487,74 @@ 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")
}
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")
}
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
Expand All @@ -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? {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
}

Expand All @@ -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
}
}

Expand All @@ -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
}
}

Expand Down
Loading

0 comments on commit 4757018

Please sign in to comment.