Skip to content

Commit 573a322

Browse files
authored
Merge pull request #100 from owenv/source-file-dep-graph
Add support for reading fine-grained source file dependency graphs
2 parents 04cdb09 + 4b724c9 commit 573a322

File tree

5 files changed

+445
-15
lines changed

5 files changed

+445
-15
lines changed

Sources/SwiftDriver/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ add_library(SwiftDriver
2222
"Incremental Compilation/IncrementalCompilation.swift"
2323
"Incremental Compilation/InputIInfoMap.swift"
2424
"Incremental Compilation/InputInfo.swift"
25+
"Incremental Compilation/SourceFileDependencyGraph.swift"
2526

2627
Jobs/AutolinkExtractJob.swift
2728
Jobs/CommandLineArguments.swift
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
//===------- SourceFileDependencyGraph.swift - Read swiftdeps files -------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
@_implementationOnly import Yams
13+
14+
public struct SourceFileDependencyGraph: Codable {
15+
public static let sourceFileProvidesInterfaceSequenceNumber: Int = 0
16+
public static let sourceFileProvidesImplementationSequenceNumber: Int = 1
17+
18+
private var allNodes: [Node]
19+
20+
public var sourceFileNodePair: (interface: Node, implementation: Node) {
21+
(interface: allNodes[SourceFileDependencyGraph.sourceFileProvidesInterfaceSequenceNumber],
22+
implementation: allNodes[SourceFileDependencyGraph.sourceFileProvidesImplementationSequenceNumber])
23+
}
24+
25+
public func forEachNode(_ doIt: (Node)->Void) {
26+
allNodes.forEach(doIt)
27+
}
28+
29+
public func forEachDefDependedUpon(by node: Node, _ doIt: (Node)->Void) {
30+
for sequenceNumber in node.defsIDependUpon {
31+
doIt(allNodes[sequenceNumber])
32+
}
33+
}
34+
35+
public func forEachArc(_ doIt: (Node, Node)->Void) {
36+
forEachNode { useNode in
37+
forEachDefDependedUpon(by: useNode) { defNode in
38+
doIt(defNode, useNode)
39+
}
40+
}
41+
}
42+
43+
@discardableResult public func verify() -> Bool {
44+
assert(Array(allNodes.indices) == allNodes.map { $0.sequenceNumber })
45+
forEachNode {
46+
$0.verify()
47+
}
48+
return true
49+
}
50+
51+
public init(contents: String) throws {
52+
let decoder = YAMLDecoder()
53+
self = try decoder.decode(Self.self, from: contents)
54+
assert(verify())
55+
}
56+
}
57+
58+
public enum DeclAspect: String, Codable {
59+
case interface, implementation
60+
}
61+
62+
public struct DependencyKey: Codable {
63+
public enum Kind: String, Codable {
64+
case topLevel
65+
case nominal
66+
case potentialMember
67+
case member
68+
case dynamicLookup
69+
case externalDepend
70+
case sourceFileProvide
71+
}
72+
73+
public var kind: Kind
74+
public var aspect: DeclAspect
75+
public var context: String
76+
public var name: String
77+
78+
public func verify() {
79+
assert(kind != .externalDepend || aspect == .interface, "All external dependencies must be interfaces.")
80+
switch kind {
81+
case .topLevel, .dynamicLookup, .externalDepend, .sourceFileProvide:
82+
assert(context.isEmpty && !name.isEmpty, "Must only have a name")
83+
case .nominal, .potentialMember:
84+
assert(!context.isEmpty && name.isEmpty, "Must only have a context")
85+
case .member:
86+
assert(!context.isEmpty && !name.isEmpty, "Must have both")
87+
}
88+
}
89+
}
90+
91+
92+
extension SourceFileDependencyGraph {
93+
public struct Node: Codable {
94+
public var key: DependencyKey
95+
public var fingerprint: String?
96+
public var sequenceNumber: Int
97+
public var defsIDependUpon: [Int]
98+
public var isProvides: Bool
99+
100+
public func verify() {
101+
key.verify()
102+
103+
if key.kind == .sourceFileProvide {
104+
switch key.aspect {
105+
case .interface:
106+
assert(sequenceNumber == SourceFileDependencyGraph.sourceFileProvidesInterfaceSequenceNumber)
107+
case .implementation:
108+
assert(sequenceNumber == SourceFileDependencyGraph.sourceFileProvidesImplementationSequenceNumber)
109+
}
110+
}
111+
}
112+
}
113+
}

Tests/SwiftDriverTests/IncrementalCompilationTests.swift

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,7 @@ import SwiftDriver
1515

1616
final class IncrementalCompilationTests: XCTestCase {
1717
func testInputInfoMapReading() throws {
18-
let contents = """
19-
version: "Apple Swift version 5.1 (swiftlang-1100.0.270.13 clang-1100.0.33.7)"
20-
options: "abbbfbcaf36b93e58efaadd8271ff142"
21-
build_time: [1570318779, 32358000]
22-
inputs:
23-
"/Volumes/AS/repos/swift-driver/sandbox/sandbox/sandbox/file2.swift": !dirty [1570318778, 0]
24-
"/Volumes/AS/repos/swift-driver/sandbox/sandbox/sandbox/main.swift": [1570083660, 0]
25-
"/Volumes/gazorp.swift": !private [0,0]
26-
"""
27-
28-
let inputInfoMap = try! InputInfoMap(contents: contents)
29-
// print(inputInfoMap.buildTime)
30-
// inputInfoMap.inputs.forEach {
31-
// print($0.key, $0.value)
32-
// }
18+
let inputInfoMap = try! InputInfoMap(contents: Inputs.inputInfoMap)
3319
XCTAssertEqual(inputInfoMap.swiftVersion,
3420
"Apple Swift version 5.1 (swiftlang-1100.0.270.13 clang-1100.0.33.7)")
3521
XCTAssertEqual(inputInfoMap.argsHash, "abbbfbcaf36b93e58efaadd8271ff142")
@@ -49,5 +35,34 @@ final class IncrementalCompilationTests: XCTestCase {
4935
previousModTime: Date(legacyDriverSecsAndNanos: [0, 0]))
5036
])
5137
}
38+
39+
func testReadSourceFileDependencyGraph() throws {
40+
let graph = try SourceFileDependencyGraph(contents: Inputs.fineGrainedSourceFileDependencyGraph)
41+
42+
graph.verify()
43+
44+
var found = false
45+
graph.forEachNode { node in
46+
guard node.sequenceNumber == 10 else { return }
47+
found = true
48+
XCTAssertEqual(node.key.kind, .nominal)
49+
XCTAssertEqual(node.key.aspect, .interface)
50+
XCTAssertEqual(node.key.context, "5hello3FooV")
51+
XCTAssertTrue(node.key.name.isEmpty)
52+
XCTAssertEqual(node.fingerprint, "8daabb8cdf69d8e8702b4788be12efd6")
53+
XCTAssertTrue(node.isProvides)
54+
55+
graph.forEachDefDependedUpon(by: node) { def in
56+
XCTAssertTrue(def.sequenceNumber == SourceFileDependencyGraph.sourceFileProvidesInterfaceSequenceNumber)
57+
XCTAssertEqual(def.key.kind, .sourceFileProvide)
58+
XCTAssertEqual(def.key.aspect, .interface)
59+
XCTAssertTrue(def.key.context.isEmpty)
60+
XCTAssertTrue(def.key.name.hasSuffix("/hello.swiftdeps"))
61+
XCTAssertEqual(def.fingerprint, "85188db3503106210367dbcb7f5d1524")
62+
XCTAssertTrue(def.isProvides)
63+
}
64+
}
65+
XCTAssertTrue(found)
66+
}
5267
}
5368

0 commit comments

Comments
 (0)