@@ -21,15 +21,15 @@ import struct TSCBasic.Diagnostic
21
21
public struct BuildRecord {
22
22
public let swiftVersion : String
23
23
/// When testing, the argsHash may be missing from the build record
24
- public let argsHash : String ?
24
+ public let argsHash : String
25
25
/// Next compile, will compare an input mod time against the start time of the previous build
26
26
public let buildStartTime : TimePoint
27
27
/// Next compile, will compare an output mod time against the end time of the previous build
28
28
public let buildEndTime : TimePoint
29
29
/// The date is the modification time of the main input file the last time the driver ran
30
30
public let inputInfos : [ VirtualPath : InputInfo ]
31
31
32
- public init ( argsHash: String ? ,
32
+ public init ( argsHash: String ,
33
33
swiftVersion: String ,
34
34
buildStartTime: TimePoint ,
35
35
buildEndTime: TimePoint ,
@@ -41,7 +41,7 @@ public struct BuildRecord {
41
41
self . inputInfos = inputInfos
42
42
}
43
43
44
- private enum SectionName : String , CaseIterable {
44
+ public enum SectionName : String , CaseIterable {
45
45
case swiftVersion = " version "
46
46
case argsHash = " options "
47
47
// Implement this for a smoother transition
@@ -60,12 +60,42 @@ public struct BuildRecord {
60
60
61
61
// MARK: - Reading the old map and deciding whether to use it
62
62
public extension BuildRecord {
63
- init ? ( contents: String , failedToReadOutOfDateMap: ( String ? ) -> Void ) {
63
+ enum Error : Swift . Error {
64
+ case malformedYAML
65
+ case invalidKey
66
+ case missingTimeStamp
67
+ case missingInputSequenceNode
68
+ case missingInputEntryNode
69
+ case missingPriorBuildState
70
+ case unexpectedKey( String )
71
+ case malformed( SectionName )
72
+
73
+ var reason : String {
74
+ switch self {
75
+ case . malformedYAML:
76
+ return " "
77
+ case . invalidKey:
78
+ return " "
79
+ case . missingTimeStamp:
80
+ return " could not read time value in build record "
81
+ case . missingInputSequenceNode:
82
+ return " no sequence node for input entry in build record "
83
+ case . missingInputEntryNode:
84
+ return " no input entry in build record "
85
+ case . missingPriorBuildState:
86
+ return " no previous build state in build record "
87
+ case . unexpectedKey( let key) :
88
+ return " Unexpected key ' \( key) ' "
89
+ case . malformed( let section) :
90
+ return " Malformed value for key ' \( section. serializedName) ' "
91
+ }
92
+ }
93
+ }
94
+ init ( contents: String ) throws {
64
95
guard let sections = try ? Parser ( yaml: contents, resolver: . basic, encoding: . utf8)
65
96
. singleRoot ( ) ? . mapping
66
97
else {
67
- failedToReadOutOfDateMap ( nil )
68
- return nil
98
+ throw Error . malformedYAML
69
99
}
70
100
var argsHash : String ?
71
101
var swiftVersion : String ?
@@ -75,60 +105,42 @@ public extension BuildRecord {
75
105
var inputInfos : [ VirtualPath : InputInfo ] ?
76
106
for (key, value) in sections {
77
107
guard let k = key. string else {
78
- failedToReadOutOfDateMap ( nil )
79
- return nil
108
+ throw Error . invalidKey
80
109
}
81
110
switch k {
82
111
case SectionName . swiftVersion. serializedName:
83
112
// There's a test that uses "" for an illegal value
84
113
guard let s = value. string, s != " " else {
85
- failedToReadOutOfDateMap ( " Malformed value for key ' \( k) ' " )
86
- return nil
114
+ break
87
115
}
88
116
swiftVersion = s
89
117
case SectionName . argsHash. serializedName:
90
118
guard let s = value. string, s != " " else {
91
- failedToReadOutOfDateMap ( " no name node in build record " )
92
- return nil
119
+ break
93
120
}
94
121
argsHash = s
95
122
case SectionName . buildStartTime. serializedName,
96
123
SectionName . legacyBuildStartTime. serializedName:
97
- guard let d = Self . decodeDate ( value,
98
- forInputInfo: false ,
99
- failedToReadOutOfDateMap)
100
- else {
101
- return nil
102
- }
103
- buildStartTime = d
124
+ buildStartTime = try Self . decodeDate ( value, forInputInfo: false )
104
125
case SectionName . buildEndTime. serializedName:
105
- guard let d = Self . decodeDate ( value,
106
- forInputInfo: false ,
107
- failedToReadOutOfDateMap)
108
- else {
109
- return nil
110
- }
111
- buildEndTime = d
126
+ buildEndTime = try Self . decodeDate ( value, forInputInfo: false )
112
127
case SectionName . inputInfos. serializedName:
113
- guard let ii = Self . decodeInputInfos ( value, failedToReadOutOfDateMap) else {
114
- return nil
115
- }
116
- inputInfos = ii
128
+ inputInfos = try Self . decodeInputInfos ( value)
117
129
default :
118
- failedToReadOutOfDateMap ( " Unexpected key ' \( k) ' " )
119
- return nil
130
+ throw Error . unexpectedKey ( k)
120
131
}
121
132
}
122
133
// The legacy driver allows argHash to be absent to ease testing.
123
134
// Mimic the legacy driver for testing ease: If no `argsHash` section,
124
135
// record still matches.
125
136
guard let sv = swiftVersion else {
126
- failedToReadOutOfDateMap ( " Malformed value for key ' \( SectionName . swiftVersion. serializedName) ' " )
127
- return nil
137
+ throw Error . malformed ( . swiftVersion)
128
138
}
129
139
guard let iis = inputInfos else {
130
- failedToReadOutOfDateMap ( " Malformed value for key ' \( SectionName . inputInfos. serializedName) ' " )
131
- return nil
140
+ throw Error . malformed ( . inputInfos)
141
+ }
142
+ guard let argsHash = argsHash else {
143
+ throw Error . malformed ( . argsHash)
132
144
}
133
145
self . init ( argsHash: argsHash,
134
146
swiftVersion: sv,
@@ -139,55 +151,40 @@ public extension BuildRecord {
139
151
140
152
private static func decodeDate(
141
153
_ node: Yams . Node ,
142
- forInputInfo: Bool ,
143
- _ failedToReadOutOfDateMap: ( String ) -> Void
144
- ) -> TimePoint ? {
154
+ forInputInfo: Bool
155
+ ) throws -> TimePoint {
145
156
guard let vals = node. sequence else {
146
- failedToReadOutOfDateMap (
147
- forInputInfo
148
- ? " no sequence node for input entry in build record "
149
- : " could not read time value in build record " )
150
- return nil
157
+ if forInputInfo {
158
+ throw Error . missingInputSequenceNode
159
+ } else {
160
+ throw Error . missingTimeStamp
161
+ }
151
162
}
152
163
guard vals. count == 2 ,
153
164
let secs = vals [ 0 ] . int,
154
165
let ns = vals [ 1 ] . int
155
166
else {
156
- failedToReadOutOfDateMap ( " could not read time value in build record " )
157
- return nil
167
+ throw Error . missingTimeStamp
158
168
}
159
169
return TimePoint ( seconds: UInt64 ( secs) , nanoseconds: UInt32 ( ns) )
160
170
}
161
171
162
172
private static func decodeInputInfos(
163
- _ node: Yams . Node ,
164
- _ failedToReadOutOfDateMap: ( String ) -> Void
165
- ) -> [ VirtualPath : InputInfo ] ? {
173
+ _ node: Yams . Node
174
+ ) throws -> [ VirtualPath : InputInfo ] {
166
175
guard let map = node. mapping else {
167
- failedToReadOutOfDateMap (
168
- " Malformed value for key ' \( SectionName . inputInfos. serializedName) ' " )
169
- return nil
176
+ throw BuildRecord . Error. malformed ( . inputInfos)
170
177
}
171
178
var infos = [ VirtualPath: InputInfo] ( )
172
179
for (keyNode, valueNode) in map {
173
180
guard let pathString = keyNode. string,
174
181
let path = try ? VirtualPath ( path: pathString)
175
182
else {
176
- failedToReadOutOfDateMap ( " no input entry in build record " )
177
- return nil
178
- }
179
- guard let previousModTime = decodeDate ( valueNode,
180
- forInputInfo: true ,
181
- failedToReadOutOfDateMap)
182
- else {
183
- return nil
184
- }
185
- guard let inputInfo = InputInfo ( tag: valueNode. tag. description,
186
- previousModTime: previousModTime,
187
- failedToReadOutOfDateMap: failedToReadOutOfDateMap)
188
- else {
189
- return nil
183
+ throw BuildRecord . Error. missingInputEntryNode
190
184
}
185
+ let previousModTime = try decodeDate ( valueNode, forInputInfo: true )
186
+ let inputInfo = try InputInfo (
187
+ tag: valueNode. tag. description, previousModTime: previousModTime)
191
188
infos [ path] = inputInfo
192
189
}
193
190
return infos
@@ -202,7 +199,7 @@ extension BuildRecord {
202
199
skippedInputs: Set < TypedVirtualPath > ? ,
203
200
compilationInputModificationDates: [ TypedVirtualPath : TimePoint ] ,
204
201
actualSwiftVersion: String ,
205
- argsHash: String ! ,
202
+ argsHash: String ,
206
203
timeBeforeFirstJob: TimePoint ,
207
204
timeAfterLastJob: TimePoint
208
205
) {
@@ -227,9 +224,7 @@ extension BuildRecord {
227
224
}
228
225
229
226
/// Pass in `currentArgsHash` to ensure it is non-nil
230
- /*@_spi(Testing)*/ public func encode( currentArgsHash: String ,
231
- diagnosticEngine: DiagnosticsEngine
232
- ) -> String ? {
227
+ public func encode( diagnosticEngine: DiagnosticsEngine ) -> String ? {
233
228
let pathsAndInfos = inputInfos. map {
234
229
input, inputInfo -> ( String , InputInfo ) in
235
230
return ( input. name, inputInfo)
@@ -241,7 +236,7 @@ extension BuildRecord {
241
236
)
242
237
let fieldNodes = [
243
238
( SectionName . swiftVersion, Yams . Node ( swiftVersion, . implicit, . doubleQuoted) ) ,
244
- ( SectionName . argsHash, Yams . Node ( currentArgsHash , . implicit, . doubleQuoted) ) ,
239
+ ( SectionName . argsHash, Yams . Node ( argsHash , . implicit, . doubleQuoted) ) ,
245
240
( SectionName . buildStartTime, Self . encode ( buildStartTime) ) ,
246
241
( SectionName . buildEndTime, Self . encode ( buildEndTime) ) ,
247
242
( SectionName . inputInfos, inputInfosNode )
@@ -287,3 +282,22 @@ extension Diagnostic.Message {
287
282
. warning( " next compile won't be incremental; could not write build record to \( path) " )
288
283
}
289
284
}
285
+
286
+
287
+ // MARK: - reading
288
+ extension InputInfo {
289
+ fileprivate init (
290
+ tag: String ,
291
+ previousModTime: TimePoint
292
+ ) throws {
293
+ guard let status = Status ( identifier: tag) else {
294
+ throw BuildRecord . Error. missingPriorBuildState
295
+ }
296
+ self . init ( status: status, previousModTime: previousModTime)
297
+ }
298
+ }
299
+
300
+ // MARK: - writing
301
+ extension InputInfo {
302
+ fileprivate var tag : String { status. identifier }
303
+ }
0 commit comments