1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
|
//===----------------------------- Tracer.swift ---------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import class TSCBasic.DiagnosticsEngine
extension ModuleDependencyGraph {
/// Trace dependencies through the graph
struct Tracer {
typealias Graph = ModuleDependencyGraph
let startingPoints: DirectlyInvalidatedNodeSet
let graph: ModuleDependencyGraph
private(set) var tracedUses = TransitivelyInvalidatedNodeArray()
/// Record the paths taking so that -driver-show-incremental can explain why things are recompiled
/// If tracing dependencies, holds a vector used to hold the current path
/// def - use/def - use/def - ...
private var currentPathIfTracing: [Node]?
private let diagnosticEngine: DiagnosticsEngine
}
}
// MARK:- Tracing
extension ModuleDependencyGraph.Tracer {
/// Find all uses of `defs` that have not already been traced.
///
/// - Parameters:
/// - defNodes: Nodes for changed declarations
/// - graph: The graph hosting the nodes
/// - diagnosticEngine: The complaint department
/// - Returns: all uses of the changed nodes that have not already been traced. These represent
/// heretofore-unschedule compilations that are now required.
static func collectPreviouslyUntracedNodesUsing(
defNodes: DirectlyInvalidatedNodeSet,
in graph: ModuleDependencyGraph,
diagnosticEngine: DiagnosticsEngine
) -> Self {
var tracer = Self(collectingUsesOf: defNodes,
in: graph,
diagnosticEngine: diagnosticEngine)
tracer.collectPreviouslyUntracedDependents()
return tracer
}
private init(collectingUsesOf defs: DirectlyInvalidatedNodeSet,
in graph: ModuleDependencyGraph,
diagnosticEngine: DiagnosticsEngine) {
self.graph = graph
self.startingPoints = defs
self.currentPathIfTracing = graph.info.reporter != nil ? [] : nil
self.diagnosticEngine = diagnosticEngine
}
private mutating func collectPreviouslyUntracedDependents() {
for n in startingPoints {
collectNextPreviouslyUntracedDependent(of: n)
}
}
private mutating func collectNextPreviouslyUntracedDependent(
of definition: ModuleDependencyGraph.Node
) {
guard definition.isUntraced else { return }
definition.setTraced()
tracedUses.append(definition)
// If this node is merely used, but not defined anywhere, nothing else
// can possibly depend upon it
if case .unknown = definition.definitionLocation { return }
let pathLengthAfterArrival = traceArrival(at: definition);
// If this use also provides something, follow it
for use in graph.nodeFinder.uses(of: definition) {
collectNextPreviouslyUntracedDependent(of: use)
}
traceDeparture(pathLengthAfterArrival);
}
private mutating func traceArrival(at visitedNode: ModuleDependencyGraph.Node
) -> Int {
guard var currentPath = currentPathIfTracing else {
return 0
}
currentPath.append(visitedNode)
currentPathIfTracing = currentPath
printPath(currentPath)
return currentPath.count
}
private mutating func traceDeparture(_ pathLengthAfterArrival: Int) {
guard var currentPath = currentPathIfTracing else { return }
assert(pathLengthAfterArrival == currentPath.count,
"Path must be maintained throughout recursive visits.")
currentPath.removeLast()
currentPathIfTracing = currentPath
}
private func printPath(_ path: [Graph.Node]) {
guard path.first?.definitionLocation != path.last?.definitionLocation
else {
return
}
graph.info.reporter?.report(
[
"Traced:",
path.compactMap { node in
guard case let .known(source) = node.definitionLocation else {
return nil
}
return source.typedFile.type == .swift
? "\(node.key.description(in: graph)) in \(source.file.basename)"
: "\(node.key.description(in: graph))"
}
.joined(separator: " -> ")
].joined(separator: " ")
)
}
}
|