Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Examples/package-info/Sources/package-info/example.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ struct Example {

let package = try await workspace.loadRootPackage(at: packagePath, observabilityScope: observability.topScope)

let graph = try workspace.loadPackageGraph(rootPath: packagePath, observabilityScope: observability.topScope)
let graph = try await workspace.loadPackageGraph(rootPath: packagePath, observabilityScope: observability.topScope)

// EXAMPLES
// ========

Expand Down
31 changes: 31 additions & 0 deletions Sources/Basics/Graph/GraphAlgorithms.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,34 @@ public func depthFirstSearch<T: Hashable>(
}
}
}

public func depthFirstSearch<T: Hashable>(
_ nodes: [T],
successors: (T) async throws -> [T],
onUnique: (T) -> Void,
onDuplicate: (T, T) -> Void
) async rethrows {
var stack = OrderedSet<T>()
var visited = Set<T>()

for node in nodes {
precondition(stack.isEmpty)
stack.append(node)

while !stack.isEmpty {
let curr = stack.removeLast()

let visitResult = visited.insert(curr)
if visitResult.inserted {
onUnique(curr)
} else {
onDuplicate(visitResult.memberAfterInsert, curr)
continue
}

for succ in try await successors(curr) {
stack.append(succ)
}
}
}
}
26 changes: 26 additions & 0 deletions Sources/Basics/Observability.swift
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,18 @@ extension DiagnosticsEmitterProtocol {
}
}

public func trap<T>(_ closure: () async throws -> T) async -> T? {
do {
return try await closure()
} catch Diagnostics.fatalError {
// FIXME: (diagnostics) deprecate this with Diagnostics.fatalError
return nil
} catch {
self.emit(error)
return nil
}
}

/// trap a throwing closure, emitting diagnostics on error and returning boolean representing success
@discardableResult
public func trap(_ closure: () throws -> Void) -> Bool {
Expand All @@ -265,6 +277,20 @@ extension DiagnosticsEmitterProtocol {
}
}

@discardableResult
public func trap(_ closure: () async throws -> Void) async -> Bool {
do {
try await closure()
return true
} catch Diagnostics.fatalError {
// FIXME: (diagnostics) deprecate this with Diagnostics.fatalError
return false
} catch {
self.emit(error)
return false
}
}

/// If `underlyingError` is not `nil`, its human-readable description is interpolated with `message`,
/// otherwise `message` itself is returned.
private func makeMessage(from message: String, underlyingError: Error?) -> String {
Expand Down
1 change: 1 addition & 0 deletions Sources/Build/BuildOperation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS
self.pkgConfigDirectories = pkgConfigDirectories
}

@available(*, noasync, message: "This must only be called from a dispatch queue")
public func getPackageGraph() throws -> ModulesGraph {
try self.packageGraph.memoize {
try self.packageGraphLoader()
Expand Down
6 changes: 3 additions & 3 deletions Sources/Commands/PackageCommands/APIDiff.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ struct DeprecatedAPIDiff: ParsableCommand {
}
}

struct APIDiff: SwiftCommand {
struct APIDiff: AsyncSwiftCommand {
static let configuration = CommandConfiguration(
commandName: "diagnose-api-breaking-changes",
abstract: "Diagnose API-breaking changes to Swift modules in a package",
Expand Down Expand Up @@ -77,7 +77,7 @@ struct APIDiff: SwiftCommand {
@Flag(help: "Regenerate the API baseline, even if an existing one is available.")
var regenerateBaseline: Bool = false

func run(_ swiftCommandState: SwiftCommandState) throws {
func run(_ swiftCommandState: SwiftCommandState) async throws {
let apiDigesterPath = try swiftCommandState.getTargetToolchain().getSwiftAPIDigester()
let apiDigesterTool = SwiftAPIDigester(fileSystem: swiftCommandState.fileSystem, tool: apiDigesterPath)

Expand Down Expand Up @@ -111,7 +111,7 @@ struct APIDiff: SwiftCommand {
observabilityScope: swiftCommandState.observabilityScope
)

let baselineDir = try baselineDumper.emitAPIBaseline(
let baselineDir = try await baselineDumper.emitAPIBaseline(
for: modulesToDiff,
at: overrideBaselineDir,
force: regenerateBaseline,
Expand Down
2 changes: 1 addition & 1 deletion Sources/Commands/PackageCommands/ArchiveSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ extension SwiftPackageCommand {
if let output {
archivePath = output
} else {
let graph = try swiftCommandState.loadPackageGraph()
let graph = try await swiftCommandState.loadPackageGraph()
let packageName = graph.rootPackages[graph.rootPackages.startIndex].manifest.displayName // TODO: use identity instead?
archivePath = packageDirectory.appending("\(packageName).zip")
}
Expand Down
10 changes: 5 additions & 5 deletions Sources/Commands/PackageCommands/CompletionCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import CoreCommands
import var TSCBasic.stdoutStream

extension SwiftPackageCommand {
struct CompletionCommand: SwiftCommand {
struct CompletionCommand: AsyncSwiftCommand {
static let configuration = CommandConfiguration(
commandName: "completion-tool",
abstract: "Completion command (for shell completions)"
Expand Down Expand Up @@ -52,7 +52,7 @@ extension SwiftPackageCommand {
@Argument(help: "generate-bash-script | generate-zsh-script |\ngenerate-fish-script | list-dependencies | list-executables")
var mode: Mode

func run(_ swiftCommandState: SwiftCommandState) throws {
func run(_ swiftCommandState: SwiftCommandState) async throws {
switch mode {
case .generateBashScript:
let script = SwiftCommand.completionScript(for: .bash)
Expand All @@ -64,7 +64,7 @@ extension SwiftPackageCommand {
let script = SwiftCommand.completionScript(for: .fish)
print(script)
case .listDependencies:
let graph = try swiftCommandState.loadPackageGraph()
let graph = try await swiftCommandState.loadPackageGraph()
// command's result output goes on stdout
// ie "swift package list-dependencies" should output to stdout
ShowDependencies.dumpDependenciesOf(
Expand All @@ -74,14 +74,14 @@ extension SwiftPackageCommand {
on: TSCBasic.stdoutStream
)
case .listExecutables:
let graph = try swiftCommandState.loadPackageGraph()
let graph = try await swiftCommandState.loadPackageGraph()
let package = graph.rootPackages[graph.rootPackages.startIndex].underlying
let executables = package.modules.filter { $0.type == .executable }
for executable in executables {
print(executable.name)
}
case .listSnippets:
let graph = try swiftCommandState.loadPackageGraph()
let graph = try await swiftCommandState.loadPackageGraph()
let package = graph.rootPackages[graph.rootPackages.startIndex].underlying
let executables = package.modules.filter { $0.type == .snippet }
for executable in executables {
Expand Down
6 changes: 3 additions & 3 deletions Sources/Commands/PackageCommands/DumpCommands.swift
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ struct DumpPackage: AsyncSwiftCommand {
}
}

struct DumpPIF: SwiftCommand {
struct DumpPIF: AsyncSwiftCommand {
// hides this command from CLI `--help` output
static let configuration = CommandConfiguration(shouldDisplay: false)

Expand All @@ -141,8 +141,8 @@ struct DumpPIF: SwiftCommand {
@Flag(help: "Preserve the internal structure of PIF")
var preserveStructure: Bool = false

func run(_ swiftCommandState: SwiftCommandState) throws {
let graph = try swiftCommandState.loadPackageGraph()
func run(_ swiftCommandState: SwiftCommandState) async throws {
let graph = try await swiftCommandState.loadPackageGraph()
let pif = try PIFBuilder.generatePIF(
buildParameters: swiftCommandState.productsBuildParameters,
packageGraph: graph,
Expand Down
16 changes: 8 additions & 8 deletions Sources/Commands/PackageCommands/EditCommands.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import CoreCommands
import SourceControl

extension SwiftPackageCommand {
struct Edit: SwiftCommand {
struct Edit: AsyncSwiftCommand {
static let configuration = CommandConfiguration(
abstract: "Put a package in editable mode")

Expand All @@ -35,12 +35,12 @@ extension SwiftPackageCommand {
@Argument(help: "The name of the package to edit")
var packageName: String

func run(_ swiftCommandState: SwiftCommandState) throws {
try swiftCommandState.resolve()
func run(_ swiftCommandState: SwiftCommandState) async throws {
try await swiftCommandState.resolve()
let workspace = try swiftCommandState.getActiveWorkspace()

// Put the dependency in edit mode.
workspace.edit(
await workspace.edit(
packageName: packageName,
path: path,
revision: revision,
Expand All @@ -50,7 +50,7 @@ extension SwiftPackageCommand {
}
}

struct Unedit: SwiftCommand {
struct Unedit: AsyncSwiftCommand {
static let configuration = CommandConfiguration(
abstract: "Remove a package from editable mode")

Expand All @@ -64,11 +64,11 @@ extension SwiftPackageCommand {
@Argument(help: "The name of the package to unedit")
var packageName: String

func run(_ swiftCommandState: SwiftCommandState) throws {
try swiftCommandState.resolve()
func run(_ swiftCommandState: SwiftCommandState) async throws {
try await swiftCommandState.resolve()
let workspace = try swiftCommandState.getActiveWorkspace()

try workspace.unedit(
try await workspace.unedit(
packageName: packageName,
forceRemove: shouldForceRemove,
root: swiftCommandState.getWorkspaceRoot(),
Expand Down
6 changes: 3 additions & 3 deletions Sources/Commands/PackageCommands/Install.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import PackageModel
import TSCBasic

extension SwiftPackageCommand {
struct Install: SwiftCommand {
struct Install: AsyncSwiftCommand {
static let configuration = CommandConfiguration(
commandName: "experimental-install",
abstract: "Offers the ability to install executable products of the current package."
Expand All @@ -30,7 +30,7 @@ extension SwiftPackageCommand {
@Option(help: "The name of the executable product to install")
var product: String?

func run(_ commandState: SwiftCommandState) throws {
func run(_ commandState: SwiftCommandState) async throws {
let swiftpmBinDir = try commandState.fileSystem.getOrCreateSwiftPMInstalledBinariesDirectory()

let env = Environment.current
Expand All @@ -49,7 +49,7 @@ extension SwiftPackageCommand {
let workspace = try commandState.getActiveWorkspace()
let packageRoot = try commandState.getPackageRoot()

let packageGraph = try workspace.loadPackageGraph(
let packageGraph = try await workspace.loadPackageGraph(
rootPath: packageRoot,
observabilityScope: commandState.observabilityScope
)
Expand Down
6 changes: 3 additions & 3 deletions Sources/Commands/PackageCommands/Learn.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import PackageGraph
import PackageModel

extension SwiftPackageCommand {
struct Learn: SwiftCommand {
struct Learn: AsyncSwiftCommand {

@OptionGroup()
var globalOptions: GlobalOptions
Expand Down Expand Up @@ -90,8 +90,8 @@ extension SwiftPackageCommand {
return snippetGroups.filter { !$0.snippets.isEmpty }
}

func run(_ swiftCommandState: SwiftCommandState) throws {
let graph = try swiftCommandState.loadPackageGraph()
func run(_ swiftCommandState: SwiftCommandState) async throws {
let graph = try await swiftCommandState.loadPackageGraph()
let package = graph.rootPackages[graph.rootPackages.startIndex]
print(package.products.map { $0.description })

Expand Down
4 changes: 2 additions & 2 deletions Sources/Commands/PackageCommands/PluginCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ struct PluginCommand: AsyncSwiftCommand {

// List the available plugins, if asked to.
if self.listCommands {
let packageGraph = try swiftCommandState.loadPackageGraph()
let packageGraph = try await swiftCommandState.loadPackageGraph()
let allPlugins = PluginCommand.availableCommandPlugins(
in: packageGraph,
limitedTo: self.pluginOptions.packageIdentity
Expand Down Expand Up @@ -181,7 +181,7 @@ struct PluginCommand: AsyncSwiftCommand {
swiftCommandState: SwiftCommandState
) async throws {
// Load the workspace and resolve the package graph.
let packageGraph = try swiftCommandState.loadPackageGraph()
let packageGraph = try await swiftCommandState.loadPackageGraph()

swiftCommandState.observabilityScope.emit(info: "Finding plugin for command ‘\(command)’")
let matchingPlugins = PluginCommand.findPlugins(matching: command, in: packageGraph, limitedTo: options.packageIdentity)
Expand Down
14 changes: 7 additions & 7 deletions Sources/Commands/PackageCommands/Resolve.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ extension SwiftPackageCommand {
var packageName: String?
}

struct Resolve: SwiftCommand {
struct Resolve: AsyncSwiftCommand {
static let configuration = CommandConfiguration(
abstract: "Resolve package dependencies")

Expand All @@ -39,11 +39,11 @@ extension SwiftPackageCommand {
@OptionGroup()
var resolveOptions: ResolveOptions

func run(_ swiftCommandState: SwiftCommandState) throws {
func run(_ swiftCommandState: SwiftCommandState) async throws {
// If a package is provided, use that to resolve the dependencies.
if let packageName = resolveOptions.packageName {
let workspace = try swiftCommandState.getActiveWorkspace()
try workspace.resolve(
try await workspace.resolve(
packageName: packageName,
root: swiftCommandState.getWorkspaceRoot(),
version: resolveOptions.version,
Expand All @@ -56,12 +56,12 @@ extension SwiftPackageCommand {
}
} else {
// Otherwise, run a normal resolve.
try swiftCommandState.resolve()
try await swiftCommandState.resolve()
}
}
}

struct Fetch: SwiftCommand {
struct Fetch: AsyncSwiftCommand {
static let configuration = CommandConfiguration(shouldDisplay: false)

@OptionGroup(visibility: .hidden)
Expand All @@ -70,11 +70,11 @@ extension SwiftPackageCommand {
@OptionGroup()
var resolveOptions: ResolveOptions

func run(_ swiftCommandState: SwiftCommandState) throws {
func run(_ swiftCommandState: SwiftCommandState) async throws {
swiftCommandState.observabilityScope.emit(warning: "'fetch' command is deprecated; use 'resolve' instead")

let resolveCommand = Resolve(globalOptions: _globalOptions, resolveOptions: _resolveOptions)
try resolveCommand.run(swiftCommandState)
try await resolveCommand.run(swiftCommandState)
}
}
}
6 changes: 3 additions & 3 deletions Sources/Commands/PackageCommands/ShowDependencies.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import protocol TSCBasic.OutputByteStream
import var TSCBasic.stdoutStream

extension SwiftPackageCommand {
struct ShowDependencies: SwiftCommand {
struct ShowDependencies: AsyncSwiftCommand {
static let configuration = CommandConfiguration(
abstract: "Print the resolved dependency graph")

Expand All @@ -35,8 +35,8 @@ extension SwiftPackageCommand {
help: "The absolute or relative path to output the resolved dependency graph.")
var outputPath: AbsolutePath?

func run(_ swiftCommandState: SwiftCommandState) throws {
let graph = try swiftCommandState.loadPackageGraph()
func run(_ swiftCommandState: SwiftCommandState) async throws {
let graph = try await swiftCommandState.loadPackageGraph()
// command's result output goes on stdout
// ie "swift package show-dependencies" should output to stdout
let stream: OutputByteStream = try outputPath.map { try LocalFileOutputByteStream($0) } ?? TSCBasic.stdoutStream
Expand Down
Loading