@@ -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,41 @@ 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
+ let targets = project. underlying. targets
58
+
59
+ for target in targets where !target. id. hasSuffix ( . dynamic) {
60
+ try container. encode ( Target ( wrapping: target) )
61
+ }
62
+
63
+ // Add *dynamic variants* at the end just to have a clear split from other targets.
64
+ for target in targets where target. id. hasSuffix ( . dynamic) {
65
+ try container. encode ( Target ( wrapping: target) )
58
66
}
59
67
}
60
68
}
61
69
}
62
-
70
+
63
71
/// Represents a high-level PIF object.
64
72
///
65
73
/// For instance, a JSON serialized *workspace* might look like this:
@@ -82,89 +90,235 @@ public enum PIF {
82
90
class var type : String {
83
91
fatalError ( " \( self ) missing implementation " )
84
92
}
85
-
86
- let type : String ?
87
-
93
+
94
+ let type : String
95
+
88
96
fileprivate init ( ) {
89
- type = Swift . type ( of : self ) . type
97
+ type = Self . type
90
98
}
91
-
99
+
92
100
fileprivate enum CodingKeys : CodingKey {
93
101
case type
94
102
case signature, contents // Used by subclasses.
95
103
}
96
-
104
+
97
105
public func encode( to encoder: Encoder ) throws {
98
106
var container = encoder. container ( keyedBy: CodingKeys . self)
99
- try container. encode ( Swift . type ( of : self ) . type, forKey: . type)
107
+ try container. encode ( type, forKey: . type)
100
108
}
101
-
109
+
102
110
required public init ( from decoder: Decoder ) throws {
103
111
let container = try decoder. container ( keyedBy: CodingKeys . self)
104
- type = try container. decode ( String . self, forKey: . type)
112
+ self . type = try container. decode ( String . self, forKey: . type)
113
+
114
+ guard self . type == Self . type else {
115
+ throw InternalError ( " Expected same type for high-level object: \( self . type) " )
116
+ }
105
117
}
106
118
}
107
-
119
+
108
120
public final class Workspace : HighLevelObject {
109
121
override class var type : String { " workspace " }
110
-
122
+
111
123
public let guid : GUID
112
124
public var name : String
113
125
public var path : AbsolutePath
114
126
public var projects : [ Project ]
115
127
var signature : String ?
116
128
117
- public init ( guid: GUID , name: String , path: AbsolutePath , projects: [ Project ] ) {
129
+ public init ( guid: GUID , name: String , path: AbsolutePath , projects: [ ProjectModel . Project ] ) {
118
130
precondition ( !guid. isEmpty)
119
131
precondition ( !name. isEmpty)
120
- precondition ( Set ( projects. map ( { $0 . guid } ) ) . count == projects. count)
121
-
132
+ precondition ( Set ( projects. map ( \ . id ) ) . count == projects. count)
133
+
122
134
self . guid = guid
123
135
self . name = name
124
136
self . path = path
125
- self . projects = projects
137
+ self . projects = projects. map { Project ( wrapping : $0 ) }
126
138
super. init ( )
127
139
}
128
-
140
+
129
141
private enum CodingKeys : CodingKey {
130
- case guid, name, path, projects, signature
142
+ case guid, name, path, projects
131
143
}
132
-
144
+
133
145
public override func encode( to encoder: Encoder ) throws {
134
146
try super. encode ( to: encoder)
147
+
135
148
var superContainer = encoder. container ( keyedBy: HighLevelObject . CodingKeys. self)
136
149
var contents = superContainer. nestedContainer ( keyedBy: CodingKeys . self, forKey: . contents)
150
+
137
151
try contents. encode ( " \( guid) @ \( schemaVersion) " , forKey: . guid)
138
152
try contents. encode ( name, forKey: . name)
139
153
try contents. encode ( path, forKey: . path)
140
-
154
+ try contents. encode ( projects. map ( \. signature) , forKey: . projects)
155
+
141
156
if encoder. userInfo. keys. contains ( . encodeForSwiftBuild) {
142
157
guard let signature else {
143
- throw InternalError ( " Expected to have workspace signature when encoding for SwiftBuild " )
158
+ throw InternalError ( " Expected to have workspace * signature* when encoding for SwiftBuild " )
144
159
}
145
160
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
161
}
150
162
}
151
-
163
+
152
164
public required init ( from decoder: Decoder ) throws {
153
165
let superContainer = try decoder. container ( keyedBy: HighLevelObject . CodingKeys. self)
154
166
let contents = try superContainer. nestedContainer ( keyedBy: CodingKeys . self, forKey: . contents)
155
-
167
+
156
168
let guidString = try contents. decode ( GUID . self, forKey: . guid)
157
169
self . guid = String ( guidString. dropLast ( " \( schemaVersion) " . count + 1 ) )
158
170
self . name = try contents. decode ( String . self, forKey: . name)
159
171
self . path = try contents. decode ( AbsolutePath . self, forKey: . path)
160
172
self . projects = try contents. decode ( [ Project ] . self, forKey: . projects)
173
+
174
+ try super. init ( from: decoder)
175
+ }
176
+ }
177
+
178
+ public final class Project : HighLevelObject {
179
+ override class var type : String { " project " }
180
+
181
+ public fileprivate( set) var underlying : ProjectModel . Project
182
+ var signature : String ?
183
+
184
+ var id : ProjectModel . GUID { underlying. id }
185
+
186
+ public init ( wrapping underlying: ProjectModel . Project ) {
187
+ precondition ( !underlying. name. isEmpty)
188
+ precondition ( !underlying. id. value. isEmpty)
189
+ precondition ( !underlying. path. isEmpty)
190
+ precondition ( !underlying. projectDir. isEmpty)
191
+ /* precondition(!underlying.developmentRegion!.isEmpty) */
192
+
193
+ precondition ( Set ( underlying. targets. map ( \. id) ) . count == underlying. targets. count)
194
+ precondition ( Set ( underlying. buildConfigs. map ( \. id) ) . count == underlying. buildConfigs. count)
195
+
196
+ self . underlying = underlying
197
+ super. init ( )
198
+ }
199
+
200
+ public override func encode( to encoder: any Encoder ) throws {
201
+ try super. encode ( to: encoder)
202
+ var superContainer = encoder. container ( keyedBy: HighLevelObject . CodingKeys. self)
203
+ try superContainer. encode ( underlying, forKey: . contents)
204
+
205
+ if encoder. userInfo. keys. contains ( . encodeForSwiftBuild) {
206
+ guard let signature else {
207
+ throw InternalError ( " Expected to have project *signature* when encoding for SwiftBuild " )
208
+ }
209
+ try superContainer. encode ( signature, forKey: . signature)
210
+ }
211
+ }
212
+
213
+ public required init ( from decoder: Decoder ) throws {
214
+ let superContainer = try decoder. container ( keyedBy: HighLevelObject . CodingKeys. self)
215
+ self . underlying = try superContainer. decode ( ProjectModel . Project. self, forKey: . contents)
216
+
217
+ try super. init ( from: decoder)
218
+ }
219
+ }
220
+
221
+ private final class Target : HighLevelObject {
222
+ override class var type : String { " target " }
223
+
224
+ public fileprivate( set) var underlying : ProjectModel . BaseTarget
225
+
226
+ var id : ProjectModel . GUID { underlying. id }
227
+
228
+ public init ( wrapping underlying: ProjectModel . BaseTarget ) {
229
+ precondition ( !underlying. id. value. isEmpty)
230
+ precondition ( !underlying. common. name. isEmpty)
231
+
232
+ self . underlying = underlying
233
+ super. init ( )
234
+ }
235
+
236
+ public override func encode( to encoder: any Encoder ) throws {
237
+ try super. encode ( to: encoder)
238
+ var superContainer = encoder. container ( keyedBy: HighLevelObject . CodingKeys. self)
239
+ try superContainer. encode ( underlying, forKey: . contents)
240
+
241
+ if encoder. userInfo. keys. contains ( . encodeForSwiftBuild) {
242
+ guard let signature = underlying. common. signature else {
243
+ throw InternalError ( " Expected to have target *signature* when encoding for SwiftBuild " )
244
+ }
245
+ try superContainer. encode ( signature, forKey: . signature)
246
+ }
247
+ }
248
+
249
+ public required init ( from decoder: Decoder ) throws {
250
+ fatalError ( " Decoding not implemented " )
251
+ /*
252
+ let superContainer = try decoder.container(keyedBy: HighLevelObject.CodingKeys.self)
253
+ self.underlying = try superContainer.decode(ProjectModel.BaseTarget.self, forKey: .contents)
254
+
161
255
try super.init(from: decoder)
256
+ */
257
+ }
258
+ }
259
+ }
260
+
261
+ extension CodingUserInfoKey {
262
+ public static let encodingPIFSignature : CodingUserInfoKey = CodingUserInfoKey ( rawValue: " encodingPIFSignature " ) !
263
+
264
+ /// Perform the encoding for SwiftBuild consumption.
265
+ public static let encodeForSwiftBuild : CodingUserInfoKey = CodingUserInfoKey ( rawValue: " encodeForXCBuild " ) !
266
+ }
267
+
268
+ // MARK: - PIF Signature Support
269
+
270
+ protocol PIFSignableObject {
271
+ var signature : String ? { get set }
272
+ var name : String { get }
273
+ }
274
+
275
+ extension PIF . Workspace : PIFSignableObject { }
276
+ extension SwiftBuild . ProjectModel . Project : PIFSignableObject {
277
+ var signature : String ? { get { " " } set { } }
278
+
279
+ }
280
+ extension SwiftBuild . ProjectModel . TargetCommon : PIFSignableObject { }
281
+
282
+ extension PIF {
283
+ /// Add signature to workspace and its high-level subobjects.
284
+ static func sign( workspace: PIF . Workspace ) throws {
285
+ let encoder = JSONEncoder . makeWithDefaults ( )
286
+
287
+ func signature( of obj: some Encodable ) throws -> String {
288
+ let signatureContent = try encoder. encode ( obj)
289
+ let signatureBytes = ByteString ( signatureContent)
290
+ let signature = signatureBytes. sha256Checksum
291
+ return signature
292
+ }
293
+
294
+ for project in workspace. projects {
295
+ for targetIndex in project. underlying. targets. indices {
296
+ let targetSignature = try signature ( of: project. underlying. targets [ targetIndex] )
297
+ project. underlying. targets [ targetIndex] . common. signature = targetSignature
298
+ }
299
+ project. signature = try signature ( of: project)
300
+ }
301
+ workspace. signature = try signature ( of: workspace)
302
+ }
303
+
304
+ static func ____sign( workspace: PIF . Workspace ) throws {
305
+ for project in workspace. projects {
306
+ for targetIndex in project. underlying. targets. indices {
307
+ let targetSignature = project. underlying. targets [ targetIndex] . id. value
308
+ project. underlying. targets [ targetIndex] . common. signature = targetSignature
309
+ }
310
+ project. signature = project. id. value
162
311
}
312
+ workspace. signature = workspace. guid
163
313
}
314
+ }
164
315
316
+
317
+ /*
318
+
165
319
/// A PIF project, consisting of a tree of groups and file references, a list of targets, and some additional
166
320
/// information.
167
- public final class Project : HighLevelObject {
321
+ public final class __Project : HighLevelObject {
168
322
override class var type: String { "project" }
169
323
170
324
public let guid: GUID
@@ -1134,8 +1288,7 @@ public enum PIF {
1134
1288
multipleValueSettings = try container.decodeIfPresent(OrderedDictionary<MultipleValueSetting, [String]>.self, forKey: .multipleValueSettings) ?? [:]
1135
1289
}
1136
1290
}
1137
- }
1138
-
1291
+
1139
1292
/// Represents a filetype recognized by the Xcode build system.
1140
1293
public struct SwiftBuildFileType: CaseIterable {
1141
1294
public static let xcassets: SwiftBuildFileType = SwiftBuildFileType(
@@ -1258,43 +1411,11 @@ extension PIF.FileReference {
1258
1411
}
1259
1412
}
1260
1413
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
- }
1274
-
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 { }
1414
+ private struct UntypedTarget: Decodable {
1415
+ struct TargetContents: Decodable {
1416
+ let type: String
1417
+ }
1418
+ let contents: TargetContents
1419
+ }
1283
1420
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
- }
1421
+ */
0 commit comments