@@ -17,6 +17,8 @@ import PackageModel
17
17
18
18
import struct TSCBasic. ByteString
19
19
20
+ import enum SwiftBuild. ProjectModel
21
+
20
22
/// The Project Interchange Format (PIF) is a structured representation of the
21
23
/// project model created by clients to send to SwiftBuild.
22
24
///
@@ -31,35 +33,35 @@ public enum PIF {
31
33
/// are represented by the objects which do not use a content-based signature scheme (workspaces and projects,
32
34
/// currently).
33
35
static let schemaVersion = 11
34
-
36
+
35
37
/// The type used for identifying PIF objects.
36
38
public typealias GUID = String
37
-
39
+
38
40
/// The top-level PIF object.
39
41
public struct TopLevelObject : Encodable {
40
42
public let workspace : PIF . Workspace
41
-
43
+
42
44
public init ( workspace: PIF . Workspace ) {
43
45
self . workspace = workspace
44
46
}
45
-
47
+
46
48
public func encode( to encoder: Encoder ) throws {
47
49
var container = encoder. unkeyedContainer ( )
48
-
50
+
49
51
// Encode the workspace.
50
52
try container. encode ( workspace)
51
-
53
+
52
54
// Encode the projects and their targets.
53
55
for project in workspace. projects {
54
56
try container. encode ( project)
55
-
56
- for target in project. targets {
57
- try container. encode ( target)
57
+
58
+ for target in project. underlying . targets {
59
+ try container. encode ( Target ( wrapping : target) )
58
60
}
59
61
}
60
62
}
61
63
}
62
-
64
+
63
65
/// Represents a high-level PIF object.
64
66
///
65
67
/// For instance, a JSON serialized *workspace* might look like this:
@@ -82,89 +84,235 @@ public enum PIF {
82
84
class var type : String {
83
85
fatalError ( " \( self ) missing implementation " )
84
86
}
85
-
86
- let type : String ?
87
-
87
+
88
+ let type : String
89
+
88
90
fileprivate init ( ) {
89
- type = Swift . type ( of : self ) . type
91
+ type = Self . type
90
92
}
91
-
93
+
92
94
fileprivate enum CodingKeys : CodingKey {
93
95
case type
94
96
case signature, contents // Used by subclasses.
95
97
}
96
-
98
+
97
99
public func encode( to encoder: Encoder ) throws {
98
100
var container = encoder. container ( keyedBy: CodingKeys . self)
99
- try container. encode ( Swift . type ( of : self ) . type, forKey: . type)
101
+ try container. encode ( type, forKey: . type)
100
102
}
101
-
103
+
102
104
required public init ( from decoder: Decoder ) throws {
103
105
let container = try decoder. container ( keyedBy: CodingKeys . self)
104
- type = try container. decode ( String . self, forKey: . type)
106
+ self . type = try container. decode ( String . self, forKey: . type)
107
+
108
+ guard self . type == Self . type else {
109
+ throw InternalError ( " Expected same type for high-level object: \( self . type) " )
110
+ }
105
111
}
106
112
}
107
-
113
+
108
114
public final class Workspace : HighLevelObject {
109
115
override class var type : String { " workspace " }
110
-
116
+
111
117
public let guid : GUID
112
118
public var name : String
113
119
public var path : AbsolutePath
114
120
public var projects : [ Project ]
115
121
var signature : String ?
116
122
117
- public init ( guid: GUID , name: String , path: AbsolutePath , projects: [ Project ] ) {
123
+ public init ( guid: GUID , name: String , path: AbsolutePath , projects: [ ProjectModel . Project ] ) {
118
124
precondition ( !guid. isEmpty)
119
125
precondition ( !name. isEmpty)
120
- precondition ( Set ( projects. map ( { $0 . guid } ) ) . count == projects. count)
121
-
126
+ precondition ( Set ( projects. map ( \ . id ) ) . count == projects. count)
127
+
122
128
self . guid = guid
123
129
self . name = name
124
130
self . path = path
125
- self . projects = projects
131
+ self . projects = projects. map { Project ( wrapping : $0 ) }
126
132
super. init ( )
127
133
}
128
-
134
+
129
135
private enum CodingKeys : CodingKey {
130
- case guid, name, path, projects, signature
136
+ case guid, name, path, projects
131
137
}
132
-
138
+
133
139
public override func encode( to encoder: Encoder ) throws {
134
140
try super. encode ( to: encoder)
141
+
135
142
var superContainer = encoder. container ( keyedBy: HighLevelObject . CodingKeys. self)
136
143
var contents = superContainer. nestedContainer ( keyedBy: CodingKeys . self, forKey: . contents)
144
+
137
145
try contents. encode ( " \( guid) @ \( schemaVersion) " , forKey: . guid)
138
146
try contents. encode ( name, forKey: . name)
139
147
try contents. encode ( path, forKey: . path)
140
-
148
+ try contents. encode ( projects. map ( \. signature) , forKey: . projects)
149
+
141
150
if encoder. userInfo. keys. contains ( . encodeForSwiftBuild) {
142
151
guard let signature else {
143
- throw InternalError ( " Expected to have workspace signature when encoding for SwiftBuild " )
152
+ throw InternalError ( " Expected to have workspace * signature* when encoding for SwiftBuild " )
144
153
}
145
154
try superContainer. encode ( signature, forKey: . signature)
146
- try contents. encode ( projects. map ( { $0. signature } ) , forKey: . projects)
147
- } else {
148
- try contents. encode ( projects, forKey: . projects)
149
155
}
150
156
}
151
-
157
+
152
158
public required init ( from decoder: Decoder ) throws {
153
159
let superContainer = try decoder. container ( keyedBy: HighLevelObject . CodingKeys. self)
154
160
let contents = try superContainer. nestedContainer ( keyedBy: CodingKeys . self, forKey: . contents)
155
-
161
+
156
162
let guidString = try contents. decode ( GUID . self, forKey: . guid)
157
163
self . guid = String ( guidString. dropLast ( " \( schemaVersion) " . count + 1 ) )
158
164
self . name = try contents. decode ( String . self, forKey: . name)
159
165
self . path = try contents. decode ( AbsolutePath . self, forKey: . path)
160
166
self . projects = try contents. decode ( [ Project ] . self, forKey: . projects)
167
+
168
+ try super. init ( from: decoder)
169
+ }
170
+ }
171
+
172
+ public final class Project : HighLevelObject {
173
+ override class var type : String { " project " }
174
+
175
+ public fileprivate( set) var underlying : ProjectModel . Project
176
+ var signature : String ?
177
+
178
+ var id : ProjectModel . GUID { underlying. id }
179
+
180
+ public init ( wrapping underlying: ProjectModel . Project ) {
181
+ precondition ( !underlying. name. isEmpty)
182
+ precondition ( !underlying. id. value. isEmpty)
183
+ precondition ( !underlying. path. isEmpty)
184
+ precondition ( !underlying. projectDir. isEmpty)
185
+ /* precondition(!underlying.developmentRegion!.isEmpty) */
186
+
187
+ precondition ( Set ( underlying. targets. map ( \. id) ) . count == underlying. targets. count)
188
+ precondition ( Set ( underlying. buildConfigs. map ( \. id) ) . count == underlying. buildConfigs. count)
189
+
190
+ self . underlying = underlying
191
+ super. init ( )
192
+ }
193
+
194
+ public override func encode( to encoder: any Encoder ) throws {
195
+ try super. encode ( to: encoder)
196
+ var superContainer = encoder. container ( keyedBy: HighLevelObject . CodingKeys. self)
197
+ try superContainer. encode ( underlying, forKey: . contents)
198
+
199
+ if encoder. userInfo. keys. contains ( . encodeForSwiftBuild) {
200
+ guard let signature else {
201
+ throw InternalError ( " Expected to have project *signature* when encoding for SwiftBuild " )
202
+ }
203
+ try superContainer. encode ( signature, forKey: . signature)
204
+ }
205
+ }
206
+
207
+ public required init ( from decoder: Decoder ) throws {
208
+ let superContainer = try decoder. container ( keyedBy: HighLevelObject . CodingKeys. self)
209
+ self . underlying = try superContainer. decode ( ProjectModel . Project. self, forKey: . contents)
210
+
161
211
try super. init ( from: decoder)
162
212
}
163
213
}
214
+
215
+ private final class Target : HighLevelObject {
216
+ override class var type : String { " target " }
217
+
218
+ public fileprivate( set) var underlying : ProjectModel . BaseTarget
219
+
220
+ var id : ProjectModel . GUID { underlying. id }
221
+
222
+ public init ( wrapping underlying: ProjectModel . BaseTarget ) {
223
+ precondition ( !underlying. id. value. isEmpty)
224
+ precondition ( !underlying. common. name. isEmpty)
225
+
226
+ self . underlying = underlying
227
+ super. init ( )
228
+ }
229
+
230
+ public override func encode( to encoder: any Encoder ) throws {
231
+ try super. encode ( to: encoder)
232
+ var superContainer = encoder. container ( keyedBy: HighLevelObject . CodingKeys. self)
233
+ try superContainer. encode ( underlying, forKey: . contents)
164
234
235
+ if encoder. userInfo. keys. contains ( . encodeForSwiftBuild) {
236
+ guard let signature = underlying. common. signature else {
237
+ throw InternalError ( " Expected to have target *signature* when encoding for SwiftBuild " )
238
+ }
239
+ try superContainer. encode ( signature, forKey: . signature)
240
+ }
241
+ }
242
+
243
+ public required init ( from decoder: Decoder ) throws {
244
+ fatalError ( " Decoding not implemented " )
245
+ /*
246
+ let superContainer = try decoder.container(keyedBy: HighLevelObject.CodingKeys.self)
247
+ self.underlying = try superContainer.decode(ProjectModel.BaseTarget.self, forKey: .contents)
248
+
249
+ try super.init(from: decoder)
250
+ */
251
+ }
252
+ }
253
+ }
254
+
255
+ extension CodingUserInfoKey {
256
+ public static let encodingPIFSignature : CodingUserInfoKey = CodingUserInfoKey ( rawValue: " encodingPIFSignature " ) !
257
+
258
+ /// Perform the encoding for SwiftBuild consumption.
259
+ public static let encodeForSwiftBuild : CodingUserInfoKey = CodingUserInfoKey ( rawValue: " encodeForXCBuild " ) !
260
+ }
261
+
262
+ // MARK: - PIF Signature Support
263
+
264
+ protocol PIFSignableObject {
265
+ var signature : String ? { get set }
266
+ var name : String { get }
267
+ }
268
+
269
+ extension PIF . Workspace : PIFSignableObject { }
270
+ extension SwiftBuild . ProjectModel . Project : PIFSignableObject {
271
+ var signature : String ? { get { " " } set { } }
272
+
273
+ }
274
+ extension SwiftBuild . ProjectModel . TargetCommon : PIFSignableObject { }
275
+
276
+ extension PIF {
277
+ /// Add signature to workspace and its high-level subobjects.
278
+ static func sign( workspace: PIF . Workspace ) throws {
279
+ let encoder = JSONEncoder . makeWithDefaults ( )
280
+
281
+ func signature( of obj: some Encodable ) throws -> String {
282
+ let signatureContent = try encoder. encode ( obj)
283
+ let signatureBytes = ByteString ( signatureContent)
284
+ let signature = signatureBytes. sha256Checksum
285
+ return signature
286
+ }
287
+
288
+ for project in workspace. projects {
289
+ for targetIndex in project. underlying. targets. indices {
290
+ let targetSignature = try signature ( of: project. underlying. targets [ targetIndex] )
291
+ project. underlying. targets [ targetIndex] . common. signature = targetSignature
292
+ }
293
+ project. signature = try signature ( of: project)
294
+ }
295
+ workspace. signature = try signature ( of: workspace)
296
+ }
297
+
298
+ static func ____sign( workspace: PIF . Workspace ) throws {
299
+ for project in workspace. projects {
300
+ for targetIndex in project. underlying. targets. indices {
301
+ let targetSignature = project. underlying. targets [ targetIndex] . id. value
302
+ project. underlying. targets [ targetIndex] . common. signature = targetSignature
303
+ }
304
+ project. signature = project. id. value
305
+ }
306
+ workspace. signature = workspace. guid
307
+ }
308
+ }
309
+
310
+
311
+ /*
312
+
165
313
/// A PIF project, consisting of a tree of groups and file references, a list of targets, and some additional
166
314
/// information.
167
- public final class Project : HighLevelObject {
315
+ public final class __Project : HighLevelObject {
168
316
override class var type: String { "project" }
169
317
170
318
public let guid: GUID
@@ -1134,8 +1282,7 @@ public enum PIF {
1134
1282
multipleValueSettings = try container.decodeIfPresent(OrderedDictionary<MultipleValueSetting, [String]>.self, forKey: .multipleValueSettings) ?? [:]
1135
1283
}
1136
1284
}
1137
- }
1138
-
1285
+
1139
1286
/// Represents a filetype recognized by the Xcode build system.
1140
1287
public struct SwiftBuildFileType: CaseIterable {
1141
1288
public static let xcassets: SwiftBuildFileType = SwiftBuildFileType(
@@ -1258,43 +1405,11 @@ extension PIF.FileReference {
1258
1405
}
1259
1406
}
1260
1407
1261
- extension CodingUserInfoKey {
1262
- public static let encodingPIFSignature : CodingUserInfoKey = CodingUserInfoKey ( rawValue: " encodingPIFSignature " ) !
1263
-
1264
- /// Perform the encoding for SwiftBuild consumption.
1265
- public static let encodeForSwiftBuild : CodingUserInfoKey = CodingUserInfoKey ( rawValue: " encodeForXCBuild " ) !
1266
- }
1267
-
1268
- private struct UntypedTarget : Decodable {
1269
- struct TargetContents : Decodable {
1270
- let type : String
1271
- }
1272
- let contents : TargetContents
1273
- }
1408
+ private struct UntypedTarget: Decodable {
1409
+ struct TargetContents: Decodable {
1410
+ let type: String
1411
+ }
1412
+ let contents: TargetContents
1413
+ }
1274
1414
1275
- // MARK: - PIF Signature Support
1276
-
1277
- protocol PIFSignableObject : AnyObject {
1278
- var signature : String ? { get set }
1279
- }
1280
- extension PIF . Workspace : PIFSignableObject { }
1281
- extension PIF . Project : PIFSignableObject { }
1282
- extension PIF . BaseTarget : PIFSignableObject { }
1283
-
1284
- extension PIF {
1285
- /// Add signature to workspace and its subobjects.
1286
- public static func sign( _ workspace: PIF . Workspace ) throws {
1287
- let encoder = JSONEncoder . makeWithDefaults ( )
1288
-
1289
- func sign< T: PIFSignableObject & Encodable > ( _ obj: T ) throws {
1290
- let signatureContent = try encoder. encode ( obj)
1291
- let bytes = ByteString ( signatureContent)
1292
- obj. signature = bytes. sha256Checksum
1293
- }
1294
-
1295
- let projects = workspace. projects
1296
- try projects. flatMap { $0. targets } . forEach ( sign)
1297
- try projects. forEach ( sign)
1298
- try sign ( workspace)
1299
- }
1300
- }
1415
+ */
0 commit comments