Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Sources/ProjectSpec/XCProjExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ extension PBXProductType {
self == .staticLibrary || self == .dynamicLibrary
}

public var isStatic: Bool {
self == .staticLibrary || self == .staticFramework
}

public var isExtension: Bool {
fileExtension == "appex"
}
Expand Down
7 changes: 4 additions & 3 deletions Sources/XcodeGenKit/PBXProjGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -964,12 +964,13 @@ public class PBXProjGenerator {
XCSwiftPackageProductDependency(productName: productName, package: packageReference)
)

// Add package dependency if linking is true.
if dependency.link ?? true {
// Xcode bakes packageProductDependencies into a static framework's
// object code, so exclude them to avoid duplicate symbols at link time.
if dependency.link ?? (target.type != .staticFramework) {
packageDependencies.append(packageDependency)
}

let link = dependency.link ?? (target.type != .staticLibrary)
let link = dependency.link ?? !target.type.isStatic
if link {
let file = PBXBuildFile(product: packageDependency, settings: getDependencyFrameworkSettings(dependency: dependency))
file.platformFilter = platform
Expand Down
57 changes: 57 additions & 0 deletions Tests/XcodeGenKitTests/ProjectGeneratorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1956,6 +1956,63 @@ class ProjectGeneratorTests: XCTestCase {
try expect(productNames).contains { $0 == "FooDomain" }
try expect(productNames).contains { $0 == "FooUI" }
}

$0.it("does not link packages in static framework targets") {
let staticFramework = Target(
name: "MyStaticFramework",
type: .staticFramework,
platform: .iOS,
dependencies: [
Dependency(type: .package(products: ["RxSwift"]), reference: "RxSwift"),
]
)

let project = Project(name: "test", targets: [staticFramework], packages: [
"RxSwift": .remote(url: "http://github.com/ReactiveX/RxSwift", versionRequirement: .exact("6.0.0")),
])

let pbxProject = try project.generatePbxProj(specValidate: false)
let nativeTarget = try unwrap(pbxProject.nativeTargets.first(where: { $0.name == staticFramework.name }))

// Package should not appear in packageProductDependencies
try expect(nativeTarget.packageProductDependencies?.isEmpty ?? true) == true

// Package should not appear in the Frameworks build phase
let frameworkPhases = nativeTarget.buildPhases.compactMap { $0 as? PBXFrameworksBuildPhase }
let linkedPackages = (frameworkPhases.first?.files ?? []).compactMap { $0.product?.productName }
try expect(linkedPackages.isEmpty) == true

// Package should appear as a build-order-only target dependency
let depProductNames = nativeTarget.dependencies.compactMap { $0.product?.productName }
try expect(depProductNames) == ["RxSwift"]
}

$0.it("links packages in static library targets' packageProductDependencies") {
let staticLibrary = Target(
name: "MyStaticLibrary",
type: .staticLibrary,
platform: .iOS,
dependencies: [
Dependency(type: .package(products: ["RxSwift"]), reference: "RxSwift"),
]
)

let project = Project(name: "test", targets: [staticLibrary], packages: [
"RxSwift": .remote(url: "http://github.com/ReactiveX/RxSwift", versionRequirement: .exact("6.0.0")),
])

let pbxProject = try project.generatePbxProj(specValidate: false)
let nativeTarget = try unwrap(pbxProject.nativeTargets.first(where: { $0.name == staticLibrary.name }))

// Package should appear in packageProductDependencies (module is importable)
let packageDepNames = nativeTarget.packageProductDependencies?.map(\.productName) ?? []
try expect(packageDepNames) == ["RxSwift"]

// Package should not appear in the Frameworks build phase
let frameworkPhases = nativeTarget.buildPhases.compactMap { $0 as? PBXFrameworksBuildPhase }
let linkedPackages = (frameworkPhases.first?.files ?? []).compactMap { $0.product?.productName }
try expect(linkedPackages.isEmpty) == true
}
}
}

Expand Down