Skip to content

Commit 0ff3b18

Browse files
authored
migrate workspace to observability APIs (#3799)
motivation: complete the transition to observability APIs changes: * update manifest loading to user observability APIs * update workspace to user observability APIs * update plugins runner to user observability APIs * add assocaited errors * make metadata non-equatable * deprecate metadata packageLocation in favour of packageKind * adjust call-sites and tests
1 parent 52c852b commit 0ff3b18

File tree

59 files changed

+2555
-2293
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+2555
-2293
lines changed

Sources/Basics/Observability.swift

Lines changed: 84 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ import Dispatch
1212
import TSCBasic
1313
import TSCUtility
1414

15-
typealias TSCDiagnostic = TSCBasic.Diagnostic
16-
1715
// this could become a struct when we remove the "errorsReported" pattern
1816

1917
// designed after https://github.com/apple/swift-log
@@ -170,25 +168,7 @@ extension DiagnosticsEmitterProtocol {
170168
}
171169

172170
public func emit(_ error: Error, metadata: ObservabilityMetadata? = .none) {
173-
var metadata = metadata
174-
// FIXME: this brings in the TSC API still
175-
if let errorProvidingLocation = error as? DiagnosticLocationProviding, let diagnosticLocation = errorProvidingLocation.diagnosticLocation {
176-
metadata = metadata ?? ObservabilityMetadata()
177-
metadata?.legacyDiagnosticLocation = .init(diagnosticLocation)
178-
}
179-
180-
let message: String
181-
// FIXME: this brings in the TSC API still
182-
// FIXME: string interpolation seems brittle
183-
if let diagnosticData = error as? DiagnosticData {
184-
message = "\(diagnosticData)"
185-
} else if let convertible = error as? DiagnosticDataConvertible {
186-
message = "\(convertible.diagnosticData)"
187-
} else {
188-
message = "\(error)"
189-
}
190-
191-
self.emit(severity: .error, message: message, metadata: metadata)
171+
self.emit(.error(error, metadata: metadata))
192172
}
193173

194174
public func emit(warning message: String, metadata: ObservabilityMetadata? = .none) {
@@ -215,6 +195,7 @@ extension DiagnosticsEmitterProtocol {
215195
self.emit(debug: message.description, metadata: metadata)
216196
}
217197

198+
/// trap a throwing closure, emitting diagnostics on error and returning the value returned by the closure
218199
public func trap<T>(_ closure: () throws -> T) -> T? {
219200
do {
220201
return try closure()
@@ -226,6 +207,21 @@ extension DiagnosticsEmitterProtocol {
226207
return nil
227208
}
228209
}
210+
211+
/// trap a throwing closure, emitting diagnostics on error and returning boolean representing success
212+
@discardableResult
213+
public func trap(_ closure: () throws -> Void) -> Bool {
214+
do {
215+
try closure()
216+
return true
217+
} catch Diagnostics.fatalError {
218+
// FIXME: (diagnostics) deprecate this with Diagnostics.fatalError
219+
return false
220+
} catch {
221+
self.emit(error)
222+
return false
223+
}
224+
}
229225
}
230226

231227
// TODO: consider using @autoclosure to delay potentially expensive evaluation of data when some diagnostics may be filtered out
@@ -245,7 +241,7 @@ public struct DiagnosticsEmitter: DiagnosticsEmitterProtocol {
245241
}
246242
}
247243

248-
public struct Diagnostic: CustomStringConvertible, Equatable {
244+
public struct Diagnostic: CustomStringConvertible {
249245
public let severity: Severity
250246
public let message: String
251247
public internal (set) var metadata: ObservabilityMetadata?
@@ -268,6 +264,31 @@ public struct Diagnostic: CustomStringConvertible, Equatable {
268264
Self(severity: .error, message: message.description, metadata: metadata)
269265
}
270266

267+
public static func error(_ error: Error, metadata: ObservabilityMetadata? = .none) -> Self {
268+
var metadata = metadata ?? ObservabilityMetadata()
269+
270+
if metadata.underlyingError == nil {
271+
metadata.underlyingError = .init(error)
272+
}
273+
// FIXME: this brings in the TSC API still
274+
if let errorProvidingLocation = error as? DiagnosticLocationProviding, let diagnosticLocation = errorProvidingLocation.diagnosticLocation {
275+
metadata.legacyDiagnosticLocation = .init(diagnosticLocation)
276+
}
277+
278+
let message: String
279+
// FIXME: this brings in the TSC API still
280+
// FIXME: string interpolation seems brittle
281+
if let diagnosticData = error as? DiagnosticData {
282+
message = "\(diagnosticData)"
283+
} else if let convertible = error as? DiagnosticDataConvertible {
284+
message = "\(convertible.diagnosticData)"
285+
} else {
286+
message = "\(error)"
287+
}
288+
289+
return Self(severity: .error, message: message, metadata: metadata)
290+
}
291+
271292
public static func warning(_ message: String, metadata: ObservabilityMetadata? = .none) -> Self {
272293
Self(severity: .warning, message: message, metadata: metadata)
273294
}
@@ -348,10 +369,10 @@ public struct Diagnostic: CustomStringConvertible, Equatable {
348369
// FIXME: we currently requires that Value conforms to CustomStringConvertible which sucks
349370
// ideally Value would conform to Equatable but that has generic requirement
350371
// luckily, this is about to change so we can clean this up soon
351-
public struct ObservabilityMetadata: Equatable, CustomDebugStringConvertible {
372+
public struct ObservabilityMetadata: CustomDebugStringConvertible {
352373
public typealias Key = ObservabilityMetadataKey
353374

354-
private var _storage = [AnyKey: CustomStringConvertible]()
375+
private var _storage = [AnyKey: Any]()
355376

356377
public init() {}
357378

@@ -382,7 +403,7 @@ public struct ObservabilityMetadata: Equatable, CustomDebugStringConvertible {
382403
///
383404
/// - Parameter body: The closure to be invoked for each item stored in this `ObservabilityMetadata`,
384405
/// passing the type-erased key and the associated value.
385-
public func forEach(_ body: (AnyKey, CustomStringConvertible) throws -> Void) rethrows {
406+
public func forEach(_ body: (AnyKey, Any) throws -> Void) rethrows {
386407
try self._storage.forEach { key, value in
387408
try body(key, value)
388409
}
@@ -402,14 +423,15 @@ public struct ObservabilityMetadata: Equatable, CustomDebugStringConvertible {
402423
public var debugDescription: String {
403424
var items = [String]()
404425
self._storage.forEach { key, value in
405-
items.append("\(key.keyType.self): \(value.description)")
426+
items.append("\(key.keyType.self): \(String(describing: value))")
406427
}
407428
return items.joined(separator: ", ")
408429
}
409430

410431
// FIXME: this currently requires that Value conforms to CustomStringConvertible which sucks
411432
// ideally Value would conform to Equatable but that has generic requirement
412433
// luckily, this is about to change so we can clean this up soon
434+
/*
413435
public static func == (lhs: ObservabilityMetadata, rhs: ObservabilityMetadata) -> Bool {
414436
if lhs.count != rhs.count {
415437
return false
@@ -424,7 +446,7 @@ public struct ObservabilityMetadata: Equatable, CustomDebugStringConvertible {
424446
}
425447

426448
return equals
427-
}
449+
}*/
428450

429451
fileprivate static func mergeLeft(_ lhs: ObservabilityMetadata?, _ rhs: ObservabilityMetadata?) -> ObservabilityMetadata? {
430452
switch (lhs, rhs) {
@@ -439,7 +461,7 @@ public struct ObservabilityMetadata: Equatable, CustomDebugStringConvertible {
439461
}
440462
}
441463

442-
//@available(*, deprecated, message: "temporary for transition DiagnosticsEngine -> DiagnosticsEmitter")
464+
@available(*, deprecated, message: "temporary for transition DiagnosticsEngine -> DiagnosticsEmitter")
443465
public func droppingLegacyKeys() -> ObservabilityMetadata? {
444466
var metadata = ObservabilityMetadata()
445467
self.forEach { (key, value) in
@@ -467,7 +489,7 @@ public struct ObservabilityMetadata: Equatable, CustomDebugStringConvertible {
467489

468490
public protocol ObservabilityMetadataKey {
469491
/// The type of value uniquely identified by this key.
470-
associatedtype Value: CustomStringConvertible
492+
associatedtype Value
471493
}
472494

473495
extension ObservabilityMetadata.AnyKey: Hashable {
@@ -480,18 +502,45 @@ extension ObservabilityMetadata.AnyKey: Hashable {
480502
}
481503
}
482504

505+
extension ObservabilityMetadata {
506+
public var underlyingError: Error? {
507+
get {
508+
self[UnderlyingErrorKey.self]
509+
}
510+
set {
511+
self[UnderlyingErrorKey.self] = newValue
512+
}
513+
}
514+
515+
private enum UnderlyingErrorKey: Key {
516+
typealias Value = Error
517+
}
518+
519+
public struct UnderlyingError: CustomStringConvertible {
520+
let underlying: Error
521+
522+
public init (_ underlying: Error) {
523+
self.underlying = underlying
524+
}
525+
526+
public var description: String {
527+
String(describing: self.underlying)
528+
}
529+
}
530+
}
531+
483532
// MARK: - Compatibility with TSC Diagnostics APIs
484533

485-
//@available(*, deprecated, message: "temporary for transition DiagnosticsEngine -> DiagnosticsEmitter")
534+
@available(*, deprecated, message: "temporary for transition DiagnosticsEngine -> DiagnosticsEmitter")
486535
extension ObservabilityScope {
487536
public func makeDiagnosticsEngine() -> DiagnosticsEngine {
488537
return .init(handlers: [{ Diagnostic($0).map{ self.diagnosticsHandler.handleDiagnostic(scope: self, diagnostic: $0) } }])
489538
}
490539
}
491540

492-
//@available(*, deprecated, message: "temporary for transition DiagnosticsEngine -> DiagnosticsEmitter")
541+
@available(*, deprecated, message: "temporary for transition DiagnosticsEngine -> DiagnosticsEmitter")
493542
extension Diagnostic {
494-
init?(_ diagnostic: TSCDiagnostic) {
543+
init?(_ diagnostic: TSCBasic.Diagnostic) {
495544
var metadata = ObservabilityMetadata()
496545
if !(diagnostic.location is UnknownLocation) {
497546
metadata.legacyDiagnosticLocation = .init(diagnostic.location)
@@ -531,7 +580,7 @@ extension ObservabilitySystem {
531580
}
532581

533582
@available(*, deprecated, message: "temporary for transition DiagnosticsEngine -> DiagnosticsEmitter")
534-
extension TSCDiagnostic {
583+
extension TSCBasic.Diagnostic {
535584
public init(_ diagnostic: Diagnostic) {
536585
let location: DiagnosticLocation
537586
if let legacyLocation = diagnostic.metadata?.legacyDiagnosticLocation {
@@ -560,7 +609,7 @@ extension TSCDiagnostic {
560609
}
561610
}
562611

563-
//@available(*, deprecated, message: "temporary for transition DiagnosticsEngine -> DiagnosticsEmitter")
612+
@available(*, deprecated, message: "temporary for transition DiagnosticsEngine -> DiagnosticsEmitter")
564613
extension ObservabilityMetadata {
565614
public var legacyDiagnosticLocation: DiagnosticLocationWrapper? {
566615
get {
@@ -588,7 +637,7 @@ extension ObservabilityMetadata {
588637
}
589638
}
590639

591-
//@available(*, deprecated, message: "temporary for transition DiagnosticsEngine -> DiagnosticsEmitter")
640+
@available(*, deprecated, message: "temporary for transition DiagnosticsEngine -> DiagnosticsEmitter")
592641
extension ObservabilityMetadata {
593642
var legacyDiagnosticData: DiagnosticDataWrapper? {
594643
get {

Sources/Basics/SQLiteBackedCache.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ public final class SQLiteBackedCache<Value: Codable>: Closable {
3434
/// - tableName: The SQLite table name. Must follow SQLite naming rules (e.g., no spaces).
3535
/// - location: SQLite.Location
3636
/// - configuration: Optional. Configuration for the cache.
37-
/// - diagnosticsEngine: DiagnosticsEngine
3837
public init(tableName: String, location: SQLite.Location, configuration: SQLiteBackedCacheConfiguration = .init()) {
3938
self.tableName = tableName
4039
self.location = location
@@ -55,7 +54,6 @@ public final class SQLiteBackedCache<Value: Codable>: Closable {
5554
/// - tableName: The SQLite table name. Must follow SQLite naming rules (e.g., no spaces).
5655
/// - path: The path of the SQLite database.
5756
/// - configuration: Optional. Configuration for the cache.
58-
/// - diagnosticsEngine: DiagnosticsEngine
5957
public convenience init(tableName: String, path: AbsolutePath, configuration: SQLiteBackedCacheConfiguration = .init()) {
6058
self.init(tableName: tableName, location: .path(path), configuration: configuration)
6159
}

Sources/Build/SPMSwiftDriverExecutor.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import Foundation
1515

1616
final class SPMSwiftDriverExecutor: DriverExecutor {
1717

18-
private enum Error: Swift.Error, DiagnosticData {
18+
private enum Error: Swift.Error, CustomStringConvertible {
1919
case inPlaceExecutionUnsupported
2020

2121
var description: String {

Sources/Commands/APIDigester.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ public struct SwiftAPIDigester {
241241
}
242242

243243
extension SwiftAPIDigester {
244-
public enum Error: Swift.Error, DiagnosticData {
244+
public enum Error: Swift.Error, CustomStringConvertible {
245245
case failedToGenerateBaseline(String)
246246

247247
public var description: String {

Sources/Commands/MultiRootSupport.swift

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,19 @@
88
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
99
*/
1010

11-
import TSCBasic
12-
import TSCUtility
13-
import class PackageModel.Manifest
11+
import Basics
1412
import Foundation
15-
1613
#if canImport(FoundationXML)
1714
import FoundationXML
1815
#endif
16+
import class PackageModel.Manifest
17+
import TSCBasic
18+
import TSCUtility
1919

2020
/// A bare minimum loader for Xcode workspaces.
2121
///
2222
/// Warning: This is only useful for debugging workspaces that contain Swift packages.
23-
public final class XcodeWorkspaceLoader {
23+
public struct XcodeWorkspaceLoader {
2424

2525
/// The parsed location.
2626
private struct Location {
@@ -34,21 +34,20 @@ public final class XcodeWorkspaceLoader {
3434
var path: String
3535
}
3636

37-
let diagnostics: DiagnosticsEngine
38-
39-
let fs: FileSystem
37+
private let fileSystem: FileSystem
38+
private let observabilityScope: ObservabilityScope
4039

41-
public init(diagnostics: DiagnosticsEngine, fs: FileSystem = localFileSystem) {
42-
self.diagnostics = diagnostics
43-
self.fs = fs
40+
public init(fileSystem: FileSystem, observabilityScope: ObservabilityScope) {
41+
self.fileSystem = fileSystem
42+
self.observabilityScope = observabilityScope
4443
}
4544

4645
/// Load the given workspace and return the file ref paths from it.
4746
public func load(workspace: AbsolutePath) throws -> [AbsolutePath] {
4847
let path = workspace.appending(component: "contents.xcworkspacedata")
49-
let contents = try Data(fs.readFileContents(path).contents)
48+
let contents = try Data(self.fileSystem.readFileContents(path).contents)
5049

51-
let delegate = ParserDelegate(diagnostics: diagnostics)
50+
let delegate = ParserDelegate(observabilityScope: self.observabilityScope)
5251
let parser = XMLParser(data: contents)
5352
parser.delegate = delegate
5453
if !parser.parse() {
@@ -67,10 +66,10 @@ public final class XcodeWorkspaceLoader {
6766
path = AbsolutePath(location.path, relativeTo: workspace.parentDirectory)
6867
}
6968

70-
if fs.exists(path.appending(component: Manifest.filename)) {
69+
if self.fileSystem.exists(path.appending(component: Manifest.filename)) {
7170
result.append(path)
7271
} else {
73-
diagnostics.emit(warning: "ignoring non-package fileref \(path)")
72+
self.observabilityScope.emit(warning: "ignoring non-package fileref \(path)")
7473
}
7574
}
7675
return result
@@ -80,10 +79,10 @@ public final class XcodeWorkspaceLoader {
8079
private class ParserDelegate: NSObject, XMLParserDelegate {
8180
var locations: [Location] = []
8281

83-
let diagnostics: DiagnosticsEngine
82+
let observabilityScope: ObservabilityScope
8483

85-
init(diagnostics: DiagnosticsEngine) {
86-
self.diagnostics = diagnostics
84+
init(observabilityScope: ObservabilityScope) {
85+
self.observabilityScope = observabilityScope
8786
}
8887

8988
func parser(
@@ -98,11 +97,11 @@ public final class XcodeWorkspaceLoader {
9897

9998
let splitted = location.split(separator: ":", maxSplits: 1, omittingEmptySubsequences: false).map(String.init)
10099
guard splitted.count == 2 else {
101-
diagnostics.emit(warning: "location split count is not two: \(splitted)")
100+
self.observabilityScope.emit(warning: "location split count is not two: \(splitted)")
102101
return
103102
}
104103
guard let kind = Location.Kind(rawValue: splitted[0]) else {
105-
diagnostics.emit(warning: "unknown kind \(splitted[0]) for location \(location)")
104+
self.observabilityScope.emit(warning: "unknown kind \(splitted[0]) for location \(location)")
106105
return
107106
}
108107

0 commit comments

Comments
 (0)