Skip to content

Commit 4408f19

Browse files
author
David Ungar
authored
Merge pull request #729 from davidungar/increment-minor
[NFC, Incremental] Test effect of a minor version number change on the priors
2 parents d47a1f2 + 5a94b6a commit 4408f19

File tree

7 files changed

+132
-17
lines changed

7 files changed

+132
-17
lines changed

Sources/SwiftDriver/Driver/Driver.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ public struct Driver {
207207
/// Info needed to write and maybe read the build record.
208208
/// Only present when the driver will be writing the record.
209209
/// Only used for reading when compiling incrementally.
210-
let buildRecordInfo: BuildRecordInfo?
210+
@_spi(Testing) public let buildRecordInfo: BuildRecordInfo?
211211

212212
/// A build-record-relative path to the location of a serialized copy of the
213213
/// driver's dependency graph.

Sources/SwiftDriver/IncrementalCompilation/BuildRecordInfo.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import SwiftOptions
3636
let buildRecordPath: VirtualPath
3737
let fileSystem: FileSystem
3838
let currentArgsHash: String
39-
let actualSwiftVersion: String
39+
@_spi(Testing) public let actualSwiftVersion: String
4040
let timeBeforeFirstJob: Date
4141
let diagnosticEngine: DiagnosticsEngine
4242
let compilationInputModificationDates: [TypedVirtualPath: Date]

Sources/SwiftDriver/IncrementalCompilation/IncrementalDependencyAndInputSetup.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,9 +218,14 @@ extension IncrementalCompilationState.IncrementalDependencyAndInputSetup {
218218
do {
219219
graphIfPresent = try ModuleDependencyGraph.read( from: dependencyGraphPath, info: self)
220220
}
221+
catch let ModuleDependencyGraph.ReadError.mismatchedSerializedGraphVersion(expected, read) {
222+
diagnosticEngine.emit(
223+
warning: "Will not do cross-module incremental builds, wrong version of priors; expected \(expected) but read \(read) at '\(dependencyGraphPath)'")
224+
graphIfPresent = nil
225+
}
221226
catch {
222227
diagnosticEngine.emit(
223-
warning: "Could not read \(dependencyGraphPath), will not do cross-module incremental builds")
228+
warning: "Will not do cross-module incremental builds, could not read priors at '\(dependencyGraphPath)'")
224229
graphIfPresent = nil
225230
}
226231
guard let graph = graphIfPresent

Sources/SwiftDriver/IncrementalCompilation/ModuleDependencyGraph.swift

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,9 @@ extension ModuleDependencyGraph {
447447
///
448448
/// - WARNING: You *must* increment the minor version number when making any
449449
/// changes to the underlying serialization format.
450-
fileprivate static let version = Version(1, 0, 0)
450+
///
451+
/// - Minor number 1: Don't serialize the `inputDepedencySourceMap`
452+
@_spi(Testing) public static let serializedGraphVersion = Version(1, 1, 0)
451453

452454
/// The IDs of the records used by the module dependency graph.
453455
fileprivate enum RecordID: UInt64 {
@@ -481,10 +483,11 @@ extension ModuleDependencyGraph {
481483
}
482484
}
483485

484-
fileprivate enum ReadError: Error {
486+
@_spi(Testing) public enum ReadError: Error {
485487
case badMagic
486488
case noRecordBlock
487489
case malformedMetadataRecord
490+
case mismatchedSerializedGraphVersion(expected: Version, read: Version)
488491
case unexpectedMetadataRecord
489492
case malformedFingerprintRecord
490493
case malformedIdentifierRecord
@@ -665,11 +668,16 @@ extension ModuleDependencyGraph {
665668
try Bitcode.read(bytes: data, using: &visitor)
666669
guard let major = visitor.majorVersion,
667670
let minor = visitor.minorVersion,
668-
visitor.compilerVersionString != nil,
669-
Version(Int(major), Int(minor), 0) == Self.version
671+
visitor.compilerVersionString != nil
670672
else {
671673
throw ReadError.malformedMetadataRecord
672674
}
675+
let readVersion = Version(Int(major), Int(minor), 0)
676+
guard readVersion == Self.serializedGraphVersion
677+
else {
678+
throw ReadError.mismatchedSerializedGraphVersion(
679+
expected: Self.serializedGraphVersion, read: readVersion)
680+
}
673681
let graph = visitor.finalizeGraph()
674682
info.reporter?.report("Read dependency graph", path)
675683
return graph
@@ -691,13 +699,17 @@ extension ModuleDependencyGraph {
691699
/// - fileSystem: The file system for this location.
692700
/// - compilerVersion: A string containing version information for the
693701
/// driver used to create this file.
702+
/// - mockSerializedGraphVersion: Overrides the standard version for testing
694703
/// - Returns: true if had error
695704
@_spi(Testing) public func write(
696705
to path: VirtualPath,
697706
on fileSystem: FileSystem,
698-
compilerVersion: String
707+
compilerVersion: String,
708+
mockSerializedGraphVersion: Version? = nil
699709
) throws {
700-
let data = ModuleDependencyGraph.Serializer.serialize(self, compilerVersion)
710+
let data = ModuleDependencyGraph.Serializer.serialize(
711+
self, compilerVersion,
712+
mockSerializedGraphVersion ?? Self.serializedGraphVersion)
701713

702714
do {
703715
try fileSystem.writeFileContents(path,
@@ -711,6 +723,7 @@ extension ModuleDependencyGraph {
711723

712724
fileprivate final class Serializer {
713725
let compilerVersion: String
726+
let serializedGraphVersion: Version
714727
let stream = BitstreamWriter()
715728
private var abbreviations = [RecordID: Bitstream.AbbreviationID]()
716729
private var identifiersToWrite = [String]()
@@ -719,8 +732,10 @@ extension ModuleDependencyGraph {
719732
fileprivate private(set) var nodeIDs = [Node: Int]()
720733
private var lastNodeID: Int = 0
721734

722-
private init(compilerVersion: String) {
735+
private init(compilerVersion: String,
736+
serializedGraphVersion: Version) {
723737
self.compilerVersion = compilerVersion
738+
self.serializedGraphVersion = serializedGraphVersion
724739
}
725740

726741
private func emitSignature() {
@@ -765,10 +780,8 @@ extension ModuleDependencyGraph {
765780
private func writeMetadata() {
766781
self.stream.writeRecord(self.abbreviations[.metadata]!, {
767782
$0.append(RecordID.metadata)
768-
// Major version
769-
$0.append(1 as UInt32)
770-
// Minor version
771-
$0.append(0 as UInt32)
783+
$0.append(serializedGraphVersion.majorForWriting)
784+
$0.append(serializedGraphVersion.minorForWriting)
772785
},
773786
blob: self.compilerVersion)
774787
}
@@ -903,9 +916,12 @@ extension ModuleDependencyGraph {
903916

904917
public static func serialize(
905918
_ graph: ModuleDependencyGraph,
906-
_ compilerVersion: String
919+
_ compilerVersion: String,
920+
_ serializedGraphVersion: Version
907921
) -> ByteString {
908-
let serializer = Serializer(compilerVersion: compilerVersion)
922+
let serializer = Serializer(
923+
compilerVersion: compilerVersion,
924+
serializedGraphVersion: serializedGraphVersion)
909925
serializer.emitSignature()
910926
serializer.writeBlockInfoBlock()
911927

@@ -1067,3 +1083,16 @@ extension Set where Element == FingerprintedExternalDependency {
10671083
self == other
10681084
}
10691085
}
1086+
1087+
fileprivate extension Version {
1088+
var majorForWriting: UInt32 {
1089+
let r = UInt32(Int64(major))
1090+
assert(Int(r) == Int(major))
1091+
return r
1092+
}
1093+
var minorForWriting: UInt32 {
1094+
let r = UInt32(Int64(minor))
1095+
assert(Int(r) == Int(minor))
1096+
return r
1097+
}
1098+
}

Tests/SwiftDriverTests/DependencyGraphSerializationTests.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,39 @@
1313
import XCTest
1414
@_spi(Testing) import SwiftDriver
1515
import TSCBasic
16+
import TSCUtility
1617

1718
class DependencyGraphSerializationTests: XCTestCase, ModuleDependencyGraphMocker {
1819
static let mockGraphCreator = MockModuleDependencyGraphCreator(maxIndex: 12)
1920

21+
/// Unit test of the `ModuleDependencyGraph` serialization
22+
///
23+
/// Ensure that a round-trip fails when the minor version number changes
24+
func testSerializedVersionChangeDetection() throws {
25+
let mockPath = VirtualPath.absolute(AbsolutePath("/module-dependency-graph"))
26+
let fs = InMemoryFileSystem()
27+
let graph = Self.mockGraphCreator.mockUpAGraph()
28+
let currentVersion = ModuleDependencyGraph.serializedGraphVersion
29+
let alteredVersion = currentVersion.withAlteredMinor
30+
try graph.write(
31+
to: mockPath,
32+
on: fs,
33+
compilerVersion: "Swift 99",
34+
mockSerializedGraphVersion: alteredVersion)
35+
do {
36+
_ = try ModuleDependencyGraph.read(from: mockPath,
37+
info: .mock(fileSystem: fs))
38+
XCTFail("Should have thrown an exception")
39+
}
40+
catch let ModuleDependencyGraph.ReadError.mismatchedSerializedGraphVersion(expected, read) {
41+
XCTAssertEqual(expected, currentVersion)
42+
XCTAssertEqual(read, alteredVersion)
43+
}
44+
catch {
45+
XCTFail("Threw an unexpected exception: \(error.localizedDescription)")
46+
}
47+
}
48+
2049
func roundTrip(_ graph: ModuleDependencyGraph) throws {
2150
let mockPath = VirtualPath.absolute(AbsolutePath("/module-dependency-graph"))
2251
let fs = InMemoryFileSystem()

Tests/SwiftDriverTests/Helpers/MockingIncrementalCompilation.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
@_spi(Testing) import SwiftDriver
1414
import TSCBasic
15+
import TSCUtility
1516
import Foundation
1617
import XCTest
1718

@@ -50,6 +51,12 @@ extension ModuleDependencyGraph {
5051
}
5152
}
5253

54+
extension Version {
55+
var withAlteredMinor: Self {
56+
Self(major, minor + 1, patch)
57+
}
58+
}
59+
5360
// MARK: - mocking
5461

5562
extension TypedVirtualPath {

Tests/SwiftDriverTests/IncrementalCompilationTests.swift

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212
import XCTest
1313
import TSCBasic
14+
import TSCUtility
1415

1516
@_spi(Testing) import SwiftDriver
1617
import SwiftOptions
@@ -297,6 +298,33 @@ extension IncrementalCompilationTests {
297298
try checkReactionToTouchingSymlinks(checkDiagnostics: true)
298299
try checkReactionToTouchingSymlinkTargets(checkDiagnostics: true)
299300
}
301+
302+
/// Ensure that the driver can detect and then recover from a priors version mismatch
303+
func testPriorsVersionDetectionAndRecovery() throws {
304+
#if !os(Linux)
305+
// create a baseline priors
306+
try buildInitialState(checkDiagnostics: true)
307+
let driver = try checkNullBuild(checkDiagnostics: true)
308+
309+
// Read the priors, change the minor version, and write it back out
310+
let outputFileMap = try driver.moduleDependencyGraph().info.outputFileMap
311+
let info = IncrementalCompilationState.IncrementalDependencyAndInputSetup
312+
.mock(outputFileMap: outputFileMap)
313+
let priorsWithOldVersion = try ModuleDependencyGraph.read(
314+
from: .absolute(priorsPath),
315+
info: info)
316+
// let priorsModTime = try localFileSystem.getFileInfo(priorsPath).modTime
317+
let compilerVersion = try XCTUnwrap(driver.buildRecordInfo).actualSwiftVersion
318+
let incrementedVersion = ModuleDependencyGraph.serializedGraphVersion.withAlteredMinor
319+
try priorsWithOldVersion?.write(to: .absolute(priorsPath),
320+
on: localFileSystem,
321+
compilerVersion: compilerVersion,
322+
mockSerializedGraphVersion: incrementedVersion)
323+
324+
try checkReactionToObsoletePriors()
325+
try checkNullBuild(checkDiagnostics: true)
326+
#endif
327+
}
300328
}
301329

302330
// MARK: - Test adding an input
@@ -454,10 +482,11 @@ extension IncrementalCompilationTests {
454482
/// - Parameters:
455483
/// - checkDiagnostics: If true verify the diagnostics
456484
/// - extraArguments: Additional command-line arguments
485+
@discardableResult
457486
private func checkNullBuild(
458487
checkDiagnostics: Bool = false,
459488
extraArguments: [String] = []
460-
) throws {
489+
) throws -> Driver {
461490
try doABuild(
462491
"as is",
463492
checkDiagnostics: checkDiagnostics,
@@ -829,6 +858,19 @@ extension IncrementalCompilationTests {
829858
return graph
830859
}
831860

861+
private func checkReactionToObsoletePriors() throws {
862+
try doABuild(
863+
"check reaction to obsolete priors",
864+
checkDiagnostics: true,
865+
extraArguments: [],
866+
whenAutolinking: autolinkLifecycleExpectedDiags) {
867+
couldNotReadPriors
868+
createdGraphFromSwiftdeps
869+
enablingCrossModule
870+
skippingAll("main", "other")
871+
}
872+
}
873+
832874
private func checkReactionToTouchingSymlinks(
833875
checkDiagnostics: Bool = false,
834876
extraArguments: [String] = []
@@ -1145,6 +1187,9 @@ extension DiagVerifiable {
11451187
@DiagsBuilder var readGraph: [Diagnostic.Message] {
11461188
"Incremental compilation: Read dependency graph"
11471189
}
1190+
@DiagsBuilder var couldNotReadPriors: [Diagnostic.Message] {
1191+
.warning("Will not do cross-module incremental builds, wrong version of priors; expected")
1192+
}
11481193
// MARK: - dependencies
11491194
@DiagsBuilder func fingerprintChanged(_ aspect: DependencyKey.DeclAspect, _ input: String) -> [Diagnostic.Message] {
11501195
"Incremental compilation: Fingerprint changed for \(aspect) of source file \(input).swiftdeps in \(input).swiftdeps"

0 commit comments

Comments
 (0)