Fix issue where frameworks with MACH_O_TYPE:staticlib were being embedded (#1003)

This commit is contained in:
Max Rabiciuc 2021-01-14 14:51:38 -08:00 committed by GitHub
parent 0eeb6369ec
commit 6b7ab91440
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 121 additions and 14 deletions

View File

@ -17,6 +17,7 @@
- Fixed error message output for `minimumXcodeGenVersion`. [#967](https://github.com/yonaskolb/XcodeGen/pull/967) @joshwalker
- Remove force-unwrapping causing crash for `LegacyTarget`s [#982](https://github.com/yonaskolb/XcodeGen/pull/982) @jcolicchio
- Fixed a race condition in an internal JSON decoder, which would occasionally fail with an error like `Parsing project spec failed: Error Domain=Unspecified error Code=0`. [#995](https://github.com/yonaskolb/XcodeGen/pull/995) @elliottwilliams
- Fixed issue where frameworks with `MACH_O_TYPE: staticlib` were being incorrectly embedded. [#1003](https://github.com/yonaskolb/XcodeGen/pull/1003) @mrabiciu
#### Internal
- Updated to Yams 4.0.0 [#984](https://github.com/yonaskolb/XcodeGen/pull/984) @swiftty

View File

@ -7,10 +7,10 @@ public enum Linkage {
case none
}
extension PBXProductType {
extension Target {
public var defaultLinkage: Linkage {
switch self {
switch type {
case .none,
.appExtension,
.application,
@ -36,8 +36,12 @@ extension PBXProductType {
.xpcService:
return .none
case .framework, .xcFramework:
// TODO: This should check `MACH_O_TYPE` in case this is a "Static Framework"
return .dynamic
// Check the MACH_O_TYPE for "Static Framework"
if settings.buildSettings.machOType == "staticlib" {
return .static
} else {
return .dynamic
}
case .dynamicLibrary:
return .dynamic
case .staticLibrary, .staticFramework:
@ -45,3 +49,10 @@ extension PBXProductType {
}
}
}
private extension BuildSettings {
var machOType: String? {
self["MACH_O_TYPE"] as? String
}
}

View File

@ -54,17 +54,16 @@ extension PBXProductType {
}
/// Function to determine when a dependendency should be embedded into the target
public func shouldEmbed(_ dependencyType: PBXProductType) -> Bool {
switch dependencyType {
case .staticLibrary, .staticFramework:
// Some dependendencies should not be embed, independently of the target type
public func shouldEmbed(_ dependencyTarget: Target) -> Bool {
switch dependencyTarget.defaultLinkage {
case .static:
// Static dependencies should never embed
return false
default:
case .dynamic, .none:
if isApp {
// If target is an app, all dependencies should be embed (except for the ones mentioned above)
// If target is an app, all dependencies should be embed (unless they're static)
return true
} else if isTest, [.framework, .bundle].contains(dependencyType) {
} else if isTest, [.framework, .bundle].contains(dependencyTarget.type) {
// If target is test, some dependencies should be embed (depending on their type)
return true
} else {

View File

@ -690,7 +690,7 @@ public class PBXProjGenerator {
}
func processTargetDependency(_ dependency: Dependency, dependencyTarget: Target, embedFileReference: PBXFileElement?) {
let dependencyLinkage = dependencyTarget.type.defaultLinkage
let dependencyLinkage = dependencyTarget.defaultLinkage
let link = dependency.link ??
((dependencyLinkage == .dynamic && target.type != .staticLibrary) ||
(dependencyLinkage == .static && target.type.isExecutable))
@ -707,7 +707,7 @@ public class PBXProjGenerator {
}
}
let embed = dependency.embed ?? target.type.shouldEmbed(dependencyTarget.type)
let embed = dependency.embed ?? target.type.shouldEmbed(dependencyTarget)
if embed {
let embedFile = addObject(
PBXBuildFile(

View File

@ -786,6 +786,102 @@ class ProjectGeneratorTests: XCTestCase {
try expect(copyFilesPhases.count) == expectedCopyFilesPhasesCount
}
}
$0.it("ensures static frameworks are not embedded by default") {
let app = Target(
name: "App",
type: .application,
platform: .iOS,
dependencies: [
Dependency(type: .target, reference: "DynamicFramework"),
Dependency(type: .target, reference: "DynamicFrameworkNotEmbedded", embed: false),
Dependency(type: .target, reference: "StaticFramework"),
Dependency(type: .target, reference: "StaticFrameworkExplicitlyEmbedded", embed: true),
Dependency(type: .target, reference: "StaticFramework2"),
Dependency(type: .target, reference: "StaticFramework2ExplicitlyEmbedded", embed: true),
Dependency(type: .target, reference: "StaticLibrary"),
]
)
let targets = [
app,
Target(
name: "DynamicFramework",
type: .framework,
platform: .iOS
),
Target(
name: "DynamicFrameworkNotEmbedded",
type: .framework,
platform: .iOS
),
Target(
name: "StaticFramework",
type: .framework,
platform: .iOS,
settings: Settings(buildSettings: ["MACH_O_TYPE": "staticlib"])
),
Target(
name: "StaticFrameworkExplicitlyEmbedded",
type: .framework,
platform: .iOS,
settings: Settings(buildSettings: ["MACH_O_TYPE": "staticlib"])
),
Target(
name: "StaticFramework2",
type: .staticFramework,
platform: .iOS
),
Target(
name: "StaticFramework2ExplicitlyEmbedded",
type: .staticFramework,
platform: .iOS
),
Target(
name: "StaticLibrary",
type: .staticLibrary,
platform: .iOS
),
]
let expectedLinkedFiles = Set([
"DynamicFramework.framework",
"DynamicFrameworkNotEmbedded.framework",
"StaticFramework.framework",
"StaticFrameworkExplicitlyEmbedded.framework",
"StaticFramework2.framework",
"StaticFramework2ExplicitlyEmbedded.framework",
"libStaticLibrary.a",
])
let expectedEmbeddedFrameworks = Set([
"DynamicFramework.framework",
"StaticFrameworkExplicitlyEmbedded.framework",
"StaticFramework2ExplicitlyEmbedded.framework"
])
let project = Project(
name: "test",
targets: targets
)
let pbxProject = try project.generatePbxProj()
let appTarget = try unwrap(pbxProject.nativeTargets.first(where: { $0.name == app.name }))
let buildPhases = appTarget.buildPhases
let frameworkPhases = pbxProject.frameworksBuildPhases.filter { buildPhases.contains($0) }
let copyFilesPhases = pbxProject.copyFilesBuildPhases.filter { buildPhases.contains($0) }
let embedFrameworkPhase = copyFilesPhases.first { $0.dstSubfolderSpec == .frameworks }
// Ensure all targets are linked
let linkFrameworks = (frameworkPhases[0].files ?? []).compactMap { $0.file?.nameOrPath }
let linkPackages = (frameworkPhases[0].files ?? []).compactMap { $0.product?.productName }
try expect(Set(linkFrameworks + linkPackages)) == expectedLinkedFiles
// Ensure only dynamic frameworks are embedded (unless there's an explicit override)
let embeddedFrameworks = Set((embedFrameworkPhase?.files ?? []).compactMap { $0.file?.nameOrPath })
try expect(embeddedFrameworks) == expectedEmbeddedFrameworks
}
$0.it("copies files only on install in the Embed Frameworks step") {
let app = Target(