Skip to content

Commit 6e60fb5

Browse files
authored
Add Build Tool Plug-ins support (#784)
- Updated `XCSwiftPackageProductDependency` to support parsing and creating build tool plugins Notes: - This is a continuation of #733, to support **Build Tool Plug-ins**. (Thanks to @technocidal)
1 parent 76fd8c3 commit 6e60fb5

File tree

4 files changed

+101
-4
lines changed

4 files changed

+101
-4
lines changed

Sources/XcodeProj/Objects/SwiftPackage/XCSwiftPackageProductDependency.swift

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,34 @@ public class XCSwiftPackageProductDependency: PBXContainerItem, PlistSerializabl
1818
}
1919
}
2020

21+
/// Is it a Plugin.
22+
var isPlugin: Bool
23+
2124
// MARK: - Init
2225

2326
public init(productName: String,
24-
package: XCRemoteSwiftPackageReference? = nil) {
27+
package: XCRemoteSwiftPackageReference? = nil,
28+
isPlugin: Bool = false) {
2529
self.productName = productName
2630
packageReference = package?.reference
31+
self.isPlugin = isPlugin
2732
super.init()
2833
}
2934

3035
public required init(from decoder: Decoder) throws {
3136
let objects = decoder.context.objects
3237
let repository = decoder.context.objectReferenceRepository
3338
let container = try decoder.container(keyedBy: CodingKeys.self)
34-
productName = try container.decode(String.self, forKey: .productName)
39+
let rawProductName = try container.decode(String.self, forKey: .productName)
40+
let pluginPrefix = "plugin:"
41+
if rawProductName.hasPrefix(pluginPrefix) {
42+
productName = String(rawProductName.dropFirst(pluginPrefix.count))
43+
isPlugin = true
44+
} else {
45+
productName = rawProductName
46+
isPlugin = false
47+
}
48+
3549
if let packageString: String = try container.decodeIfPresent(.package) {
3650
packageReference = repository.getOrCreate(reference: packageString, objects: objects)
3751
} else {
@@ -46,7 +60,11 @@ public class XCSwiftPackageProductDependency: PBXContainerItem, PlistSerializabl
4660
if let package = package {
4761
dictionary["package"] = .string(.init(package.reference.value, comment: "XCRemoteSwiftPackageReference \"\(package.name ?? "")\""))
4862
}
49-
dictionary["productName"] = .string(.init(productName))
63+
if isPlugin {
64+
dictionary["productName"] = .string(.init("plugin:" + productName))
65+
} else {
66+
dictionary["productName"] = .string(.init(productName))
67+
}
5068

5169
return (key: CommentedString(reference, comment: productName),
5270
value: .dictionary(dictionary))

Sources/XcodeProj/Utils/ReferenceGenerator.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,15 @@ final class ReferenceGenerator: ReferenceGenerating {
9999
fixReference(for: $0, identifiers: identifiers)
100100
}
101101

102+
// Build Tool Plug-ins
103+
target.dependencies.forEach {
104+
guard let product = $0.product, product.isPlugin else { return }
105+
106+
var identifiers = identifiers
107+
identifiers.append(product.productName)
108+
fixReference(for: product, identifiers: identifiers)
109+
}
110+
102111
fixReference(for: target, identifiers: identifiers)
103112
}
104113
}

Tests/XcodeProjTests/Objects/SwiftPackage/XCSwiftPackageProductDependencyTests.swift

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,25 @@ final class XCSwiftPackageProductDependencyTests: XCTestCase {
1919
// Then
2020
XCTAssertEqual(got.productName, "xcodeproj")
2121
XCTAssertEqual(got.packageReference?.value, "packageReference")
22+
XCTAssertEqual(got.isPlugin, false)
23+
}
24+
25+
func test_initAsPlugin() throws {
26+
// Given
27+
let decoder = XcodeprojPropertyListDecoder()
28+
let encoder = PropertyListEncoder()
29+
let plist = ["reference": "reference",
30+
"productName": "plugin:xcodeproj",
31+
"package": "packageReference"]
32+
let data = try encoder.encode(plist)
33+
34+
// When
35+
let got = try decoder.decode(XCSwiftPackageProductDependency.self, from: data)
36+
37+
// Then
38+
XCTAssertEqual(got.productName, "xcodeproj")
39+
XCTAssertEqual(got.packageReference?.value, "packageReference")
40+
XCTAssertEqual(got.isPlugin, true)
2241
}
2342

2443
func test_plistValues() throws {
@@ -39,6 +58,25 @@ final class XCSwiftPackageProductDependencyTests: XCTestCase {
3958
]))
4059
}
4160

61+
func test_plistValuesAsPlugin() throws {
62+
// Given
63+
let proj = PBXProj()
64+
let package = XCRemoteSwiftPackageReference(repositoryURL: "repository")
65+
let subject = XCSwiftPackageProductDependency(productName: "product",
66+
package: package,
67+
isPlugin: true)
68+
69+
// When
70+
let got = try subject.plistKeyAndValue(proj: proj, reference: "reference")
71+
72+
// Then
73+
XCTAssertEqual(got.value, .dictionary([
74+
"isa": "XCSwiftPackageProductDependency",
75+
"productName": "plugin:product",
76+
"package": .string(.init(package.reference.value, comment: "XCRemoteSwiftPackageReference \"\(package.name ?? "")\"")),
77+
]))
78+
}
79+
4280
func test_equal() {
4381
// Given
4482
let package = XCRemoteSwiftPackageReference(repositoryURL: "repository")
@@ -50,4 +88,20 @@ final class XCSwiftPackageProductDependencyTests: XCTestCase {
5088
// Then
5189
XCTAssertEqual(first, second)
5290
}
91+
92+
func test_isPlugin() {
93+
// Given
94+
let plugin = XCSwiftPackageProductDependency(productName: "product", isPlugin: true)
95+
96+
// Then
97+
XCTAssertTrue(plugin.isPlugin)
98+
}
99+
100+
func test_isNotPlugin() {
101+
// Given
102+
let plugin = XCSwiftPackageProductDependency(productName: "product")
103+
104+
// Then
105+
XCTAssertFalse(plugin.isPlugin)
106+
}
53107
}

Tests/XcodeProjTests/Utils/ReferenceGeneratorTests.swift

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,13 @@ class ReferenceGeneratorTests: XCTestCase {
1111
let containerItemProxy = project.makeContainerItemProxy(fileReference: remoteProjectFileReference)
1212
let productReferenceProxy = project.makeReferenceProxy(containerItemProxy: containerItemProxy)
1313
let productsGroup = project.makeProductsGroup(children: [productReferenceProxy])
14+
let pluginDependency = project.makePluginDependency()
15+
let (target, _) = project.makeTarget(productReferenceProxy: productReferenceProxy)
16+
target.dependencies.append(pluginDependency)
1417

1518
pbxProject.projectReferences.append(["ProductGroup": productsGroup.reference,
1619
"ProjectRef": remoteProjectFileReference.reference])
20+
pbxProject.targets.append(target)
1721

1822
let referenceGenerator = ReferenceGenerator(outputSettings: PBXOutputSettings())
1923
try referenceGenerator.generateReferences(proj: project)
@@ -22,6 +26,7 @@ class ReferenceGeneratorTests: XCTestCase {
2226
XCTAssert(!containerItemProxy.reference.temporary)
2327
XCTAssert(!productReferenceProxy.reference.temporary)
2428
XCTAssert(!remoteProjectFileReference.reference.temporary)
29+
XCTAssert(!pluginDependency.productReference!.temporary)
2530
}
2631

2732
func test_projectReferencingRemoteXcodeprojBundle_generatesDeterministicIdentifiers() throws {
@@ -32,7 +37,9 @@ class ReferenceGeneratorTests: XCTestCase {
3237
let containerItemProxy = project.makeContainerItemProxy(fileReference: remoteProjectFileReference)
3338
let productReferenceProxy = project.makeReferenceProxy(containerItemProxy: containerItemProxy)
3439
let productsGroup = project.makeProductsGroup(children: [productReferenceProxy])
40+
let pluginDependency = project.makePluginDependency()
3541
let (target, buildFile) = project.makeTarget(productReferenceProxy: productReferenceProxy)
42+
target.dependencies.append(pluginDependency)
3643

3744
pbxProject.projectReferences.append(["ProductGroup": productsGroup.reference,
3845
"ProjectRef": remoteProjectFileReference.reference])
@@ -41,7 +48,7 @@ class ReferenceGeneratorTests: XCTestCase {
4148
let referenceGenerator = ReferenceGenerator(outputSettings: PBXOutputSettings())
4249
try referenceGenerator.generateReferences(proj: project)
4350

44-
return [remoteProjectFileReference, containerItemProxy, productReferenceProxy, productsGroup, buildFile]
51+
return [remoteProjectFileReference, containerItemProxy, productReferenceProxy, productsGroup, buildFile, pluginDependency.productReference!.getObject()!]
4552
.map { $0.reference.value }
4653
}
4754

@@ -132,6 +139,15 @@ private extension PBXProj {
132139
return productsGroup
133140
}
134141

142+
func makePluginDependency() -> PBXTargetDependency {
143+
let packageReference = XCRemoteSwiftPackageReference(repositoryURL: "repository")
144+
let packageDependency = XCSwiftPackageProductDependency(productName: "product", package: packageReference, isPlugin: true)
145+
let targetDependency = PBXTargetDependency(product: packageDependency)
146+
add(object: targetDependency.productReference!.getObject()!)
147+
148+
return targetDependency
149+
}
150+
135151
func makeTarget(productReferenceProxy: PBXReferenceProxy) -> (target: PBXTarget, buildFile: PBXBuildFile) {
136152
let buildFile = PBXBuildFile(file: productReferenceProxy)
137153
add(object: buildFile)

0 commit comments

Comments
 (0)