Skip to content

Commit

Permalink
Add Build Tool Plug-ins support (#784)
Browse files Browse the repository at this point in the history
- 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)
  • Loading branch information
BarredEwe authored Jul 27, 2023
1 parent 76fd8c3 commit 6e60fb5
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,34 @@ public class XCSwiftPackageProductDependency: PBXContainerItem, PlistSerializabl
}
}

/// Is it a Plugin.
var isPlugin: Bool

// MARK: - Init

public init(productName: String,
package: XCRemoteSwiftPackageReference? = nil) {
package: XCRemoteSwiftPackageReference? = nil,
isPlugin: Bool = false) {
self.productName = productName
packageReference = package?.reference
self.isPlugin = isPlugin
super.init()
}

public required init(from decoder: Decoder) throws {
let objects = decoder.context.objects
let repository = decoder.context.objectReferenceRepository
let container = try decoder.container(keyedBy: CodingKeys.self)
productName = try container.decode(String.self, forKey: .productName)
let rawProductName = try container.decode(String.self, forKey: .productName)
let pluginPrefix = "plugin:"
if rawProductName.hasPrefix(pluginPrefix) {
productName = String(rawProductName.dropFirst(pluginPrefix.count))
isPlugin = true
} else {
productName = rawProductName
isPlugin = false
}

if let packageString: String = try container.decodeIfPresent(.package) {
packageReference = repository.getOrCreate(reference: packageString, objects: objects)
} else {
Expand All @@ -46,7 +60,11 @@ public class XCSwiftPackageProductDependency: PBXContainerItem, PlistSerializabl
if let package = package {
dictionary["package"] = .string(.init(package.reference.value, comment: "XCRemoteSwiftPackageReference \"\(package.name ?? "")\""))
}
dictionary["productName"] = .string(.init(productName))
if isPlugin {
dictionary["productName"] = .string(.init("plugin:" + productName))
} else {
dictionary["productName"] = .string(.init(productName))
}

return (key: CommentedString(reference, comment: productName),
value: .dictionary(dictionary))
Expand Down
9 changes: 9 additions & 0 deletions Sources/XcodeProj/Utils/ReferenceGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,15 @@ final class ReferenceGenerator: ReferenceGenerating {
fixReference(for: $0, identifiers: identifiers)
}

// Build Tool Plug-ins
target.dependencies.forEach {
guard let product = $0.product, product.isPlugin else { return }

var identifiers = identifiers
identifiers.append(product.productName)
fixReference(for: product, identifiers: identifiers)
}

fixReference(for: target, identifiers: identifiers)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,25 @@ final class XCSwiftPackageProductDependencyTests: XCTestCase {
// Then
XCTAssertEqual(got.productName, "xcodeproj")
XCTAssertEqual(got.packageReference?.value, "packageReference")
XCTAssertEqual(got.isPlugin, false)
}

func test_initAsPlugin() throws {
// Given
let decoder = XcodeprojPropertyListDecoder()
let encoder = PropertyListEncoder()
let plist = ["reference": "reference",
"productName": "plugin:xcodeproj",
"package": "packageReference"]
let data = try encoder.encode(plist)

// When
let got = try decoder.decode(XCSwiftPackageProductDependency.self, from: data)

// Then
XCTAssertEqual(got.productName, "xcodeproj")
XCTAssertEqual(got.packageReference?.value, "packageReference")
XCTAssertEqual(got.isPlugin, true)
}

func test_plistValues() throws {
Expand All @@ -39,6 +58,25 @@ final class XCSwiftPackageProductDependencyTests: XCTestCase {
]))
}

func test_plistValuesAsPlugin() throws {
// Given
let proj = PBXProj()
let package = XCRemoteSwiftPackageReference(repositoryURL: "repository")
let subject = XCSwiftPackageProductDependency(productName: "product",
package: package,
isPlugin: true)

// When
let got = try subject.plistKeyAndValue(proj: proj, reference: "reference")

// Then
XCTAssertEqual(got.value, .dictionary([
"isa": "XCSwiftPackageProductDependency",
"productName": "plugin:product",
"package": .string(.init(package.reference.value, comment: "XCRemoteSwiftPackageReference \"\(package.name ?? "")\"")),
]))
}

func test_equal() {
// Given
let package = XCRemoteSwiftPackageReference(repositoryURL: "repository")
Expand All @@ -50,4 +88,20 @@ final class XCSwiftPackageProductDependencyTests: XCTestCase {
// Then
XCTAssertEqual(first, second)
}

func test_isPlugin() {
// Given
let plugin = XCSwiftPackageProductDependency(productName: "product", isPlugin: true)

// Then
XCTAssertTrue(plugin.isPlugin)
}

func test_isNotPlugin() {
// Given
let plugin = XCSwiftPackageProductDependency(productName: "product")

// Then
XCTAssertFalse(plugin.isPlugin)
}
}
18 changes: 17 additions & 1 deletion Tests/XcodeProjTests/Utils/ReferenceGeneratorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,13 @@ class ReferenceGeneratorTests: XCTestCase {
let containerItemProxy = project.makeContainerItemProxy(fileReference: remoteProjectFileReference)
let productReferenceProxy = project.makeReferenceProxy(containerItemProxy: containerItemProxy)
let productsGroup = project.makeProductsGroup(children: [productReferenceProxy])
let pluginDependency = project.makePluginDependency()
let (target, _) = project.makeTarget(productReferenceProxy: productReferenceProxy)
target.dependencies.append(pluginDependency)

pbxProject.projectReferences.append(["ProductGroup": productsGroup.reference,
"ProjectRef": remoteProjectFileReference.reference])
pbxProject.targets.append(target)

let referenceGenerator = ReferenceGenerator(outputSettings: PBXOutputSettings())
try referenceGenerator.generateReferences(proj: project)
Expand All @@ -22,6 +26,7 @@ class ReferenceGeneratorTests: XCTestCase {
XCTAssert(!containerItemProxy.reference.temporary)
XCTAssert(!productReferenceProxy.reference.temporary)
XCTAssert(!remoteProjectFileReference.reference.temporary)
XCTAssert(!pluginDependency.productReference!.temporary)
}

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

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

return [remoteProjectFileReference, containerItemProxy, productReferenceProxy, productsGroup, buildFile]
return [remoteProjectFileReference, containerItemProxy, productReferenceProxy, productsGroup, buildFile, pluginDependency.productReference!.getObject()!]
.map { $0.reference.value }
}

Expand Down Expand Up @@ -132,6 +139,15 @@ private extension PBXProj {
return productsGroup
}

func makePluginDependency() -> PBXTargetDependency {
let packageReference = XCRemoteSwiftPackageReference(repositoryURL: "repository")
let packageDependency = XCSwiftPackageProductDependency(productName: "product", package: packageReference, isPlugin: true)
let targetDependency = PBXTargetDependency(product: packageDependency)
add(object: targetDependency.productReference!.getObject()!)

return targetDependency
}

func makeTarget(productReferenceProxy: PBXReferenceProxy) -> (target: PBXTarget, buildFile: PBXBuildFile) {
let buildFile = PBXBuildFile(file: productReferenceProxy)
add(object: buildFile)
Expand Down

0 comments on commit 6e60fb5

Please sign in to comment.