Skip to content

Commit 8bee0fc

Browse files
committed
WIP: Adopt new PIF builder in SwiftBuildSupport
1 parent c8d439a commit 8bee0fc

11 files changed

+489
-227
lines changed

Sources/SwiftBuildSupport/BuildSystem.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ extension BuildSubset {
1414
var pifTargetName: String {
1515
switch self {
1616
case .product(let name, _):
17-
_PackagePIFProjectBuilder.targetName(for: name)
17+
PackagePIFBuilder.targetName(forProductName: name)
1818
case .target(let name, _):
1919
name
2020
case .allExcludingTests:

Sources/SwiftBuildSupport/PIF.swift

+192-77
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import PackageModel
1717

1818
import struct TSCBasic.ByteString
1919

20+
import enum SwiftBuild.ProjectModel
21+
2022
/// The Project Interchange Format (PIF) is a structured representation of the
2123
/// project model created by clients to send to SwiftBuild.
2224
///
@@ -31,35 +33,35 @@ public enum PIF {
3133
/// are represented by the objects which do not use a content-based signature scheme (workspaces and projects,
3234
/// currently).
3335
static let schemaVersion = 11
34-
36+
3537
/// The type used for identifying PIF objects.
3638
public typealias GUID = String
37-
39+
3840
/// The top-level PIF object.
3941
public struct TopLevelObject: Encodable {
4042
public let workspace: PIF.Workspace
41-
43+
4244
public init(workspace: PIF.Workspace) {
4345
self.workspace = workspace
4446
}
45-
47+
4648
public func encode(to encoder: Encoder) throws {
4749
var container = encoder.unkeyedContainer()
48-
50+
4951
// Encode the workspace.
5052
try container.encode(workspace)
51-
53+
5254
// Encode the projects and their targets.
5355
for project in workspace.projects {
5456
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))
5860
}
5961
}
6062
}
6163
}
62-
64+
6365
/// Represents a high-level PIF object.
6466
///
6567
/// For instance, a JSON serialized *workspace* might look like this:
@@ -82,89 +84,235 @@ public enum PIF {
8284
class var type: String {
8385
fatalError("\(self) missing implementation")
8486
}
85-
86-
let type: String?
87-
87+
88+
let type: String
89+
8890
fileprivate init() {
89-
type = Swift.type(of: self).type
91+
type = Self.type
9092
}
91-
93+
9294
fileprivate enum CodingKeys: CodingKey {
9395
case type
9496
case signature, contents // Used by subclasses.
9597
}
96-
98+
9799
public func encode(to encoder: Encoder) throws {
98100
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)
100102
}
101-
103+
102104
required public init(from decoder: Decoder) throws {
103105
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+
}
105111
}
106112
}
107-
113+
108114
public final class Workspace: HighLevelObject {
109115
override class var type: String { "workspace" }
110-
116+
111117
public let guid: GUID
112118
public var name: String
113119
public var path: AbsolutePath
114120
public var projects: [Project]
115121
var signature: String?
116122

117-
public init(guid: GUID, name: String, path: AbsolutePath, projects: [Project]) {
123+
public init(guid: GUID, name: String, path: AbsolutePath, projects: [ProjectModel.Project]) {
118124
precondition(!guid.isEmpty)
119125
precondition(!name.isEmpty)
120-
precondition(Set(projects.map({ $0.guid })).count == projects.count)
121-
126+
precondition(Set(projects.map(\.id)).count == projects.count)
127+
122128
self.guid = guid
123129
self.name = name
124130
self.path = path
125-
self.projects = projects
131+
self.projects = projects.map { Project(wrapping: $0) }
126132
super.init()
127133
}
128-
134+
129135
private enum CodingKeys: CodingKey {
130-
case guid, name, path, projects, signature
136+
case guid, name, path, projects
131137
}
132-
138+
133139
public override func encode(to encoder: Encoder) throws {
134140
try super.encode(to: encoder)
141+
135142
var superContainer = encoder.container(keyedBy: HighLevelObject.CodingKeys.self)
136143
var contents = superContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: .contents)
144+
137145
try contents.encode("\(guid)@\(schemaVersion)", forKey: .guid)
138146
try contents.encode(name, forKey: .name)
139147
try contents.encode(path, forKey: .path)
140-
148+
try contents.encode(projects.map(\.signature), forKey: .projects)
149+
141150
if encoder.userInfo.keys.contains(.encodeForSwiftBuild) {
142151
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")
144153
}
145154
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)
149155
}
150156
}
151-
157+
152158
public required init(from decoder: Decoder) throws {
153159
let superContainer = try decoder.container(keyedBy: HighLevelObject.CodingKeys.self)
154160
let contents = try superContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: .contents)
155-
161+
156162
let guidString = try contents.decode(GUID.self, forKey: .guid)
157163
self.guid = String(guidString.dropLast("\(schemaVersion)".count + 1))
158164
self.name = try contents.decode(String.self, forKey: .name)
159165
self.path = try contents.decode(AbsolutePath.self, forKey: .path)
160166
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+
161211
try super.init(from: decoder)
162212
}
163213
}
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)
164234

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+
165313
/// A PIF project, consisting of a tree of groups and file references, a list of targets, and some additional
166314
/// information.
167-
public final class Project: HighLevelObject {
315+
public final class __Project: HighLevelObject {
168316
override class var type: String { "project" }
169317

170318
public let guid: GUID
@@ -1134,8 +1282,7 @@ public enum PIF {
11341282
multipleValueSettings = try container.decodeIfPresent(OrderedDictionary<MultipleValueSetting, [String]>.self, forKey: .multipleValueSettings) ?? [:]
11351283
}
11361284
}
1137-
}
1138-
1285+
11391286
/// Represents a filetype recognized by the Xcode build system.
11401287
public struct SwiftBuildFileType: CaseIterable {
11411288
public static let xcassets: SwiftBuildFileType = SwiftBuildFileType(
@@ -1258,43 +1405,11 @@ extension PIF.FileReference {
12581405
}
12591406
}
12601407

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+
}
12741414

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

Comments
 (0)