Skip to content

Use carthage xcframeworks #1020

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
8 changes: 8 additions & 0 deletions Sources/ProjectSpec/SpecOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ public struct SpecOptions: Equatable {
public static let generateEmptyDirectoriesDefault = false
public static let findCarthageFrameworksDefault = false
public static let useBaseInternationalizationDefault = true
public static let carthageXCFrameworksDefault = false
public static let schemePathPrefixDefault = "../../"

public var minimumXcodeGenVersion: Version?
public var carthageXCFrameworks: Bool
public var carthageBuildPath: String?
public var carthageExecutablePath: String?
public var createIntermediateGroups: Bool
Expand Down Expand Up @@ -76,6 +78,7 @@ public struct SpecOptions: Equatable {

public init(
minimumXcodeGenVersion: Version? = nil,
carthageXCFrameworks: Bool = carthageXCFrameworksDefault,
carthageBuildPath: String? = nil,
carthageExecutablePath: String? = nil,
createIntermediateGroups: Bool = createIntermediateGroupsDefault,
Expand All @@ -102,6 +105,7 @@ public struct SpecOptions: Equatable {
schemePathPrefix: String = schemePathPrefixDefault
) {
self.minimumXcodeGenVersion = minimumXcodeGenVersion
self.carthageXCFrameworks = carthageXCFrameworks
self.carthageBuildPath = carthageBuildPath
self.carthageExecutablePath = carthageExecutablePath
self.createIntermediateGroups = createIntermediateGroups
Expand Down Expand Up @@ -136,6 +140,7 @@ extension SpecOptions: JSONObjectConvertible {
minimumXcodeGenVersion = try Version.parse(string)
}

carthageXCFrameworks = jsonDictionary.json(atKeyPath: "carthageXCFrameworks") ?? SpecOptions.carthageXCFrameworksDefault
carthageBuildPath = jsonDictionary.json(atKeyPath: "carthageBuildPath")
carthageExecutablePath = jsonDictionary.json(atKeyPath: "carthageExecutablePath")
bundleIdPrefix = jsonDictionary.json(atKeyPath: "bundleIdPrefix")
Expand Down Expand Up @@ -205,6 +210,9 @@ extension SpecOptions: JSONEncodable {
if useBaseInternationalization != SpecOptions.useBaseInternationalizationDefault {
dict["useBaseInternationalization"] = useBaseInternationalization
}
if carthageXCFrameworks != SpecOptions.carthageXCFrameworksDefault {
dict["carthageXCFrameworks"] = carthageXCFrameworks
}
if schemePathPrefix != SpecOptions.schemePathPrefixDefault {
dict["schemePathPrefix"] = schemePathPrefix
}
Expand Down
35 changes: 32 additions & 3 deletions Sources/XcodeGenKit/CarthageDependencyResolver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,43 @@ public class CarthageDependencyResolver {
versionLoader = CarthageVersionLoader(buildPath: project.basePath + CarthageDependencyResolver.getBuildPath(project))
}

func frameworkPath(for name: String, platform: Platform, linkType: Dependency.CarthageLinkType) -> Path {
var file = Path(name)
var xcFramework = project.options.carthageXCFrameworks
switch file.extension {
case "xcframework":
xcFramework = true
case "framework":
xcFramework = false
case .none:
let fileExtension: String
if xcFramework {
fileExtension = "xcframework"
} else {
fileExtension = "framework"
}
file = Path(file.string + ".\(fileExtension)")
default:
break
}

let parentPath = Path(buildPath(for: platform, linkType: linkType, xcFramework: xcFramework))
return parentPath + file
}

/// Carthage's build path for the given platform
func buildPath(for platform: Platform, linkType: Dependency.CarthageLinkType) -> String {
func buildPath(for platform: Platform, linkType: Dependency.CarthageLinkType, xcFramework: Bool) -> String {
var path = buildPath
if !xcFramework {
path += "/\(platform.carthageName)"
}
switch linkType {
case .static:
return "\(buildPath)/\(platform.carthageName)/Static"
path += "/Static"
case .dynamic:
return "\(buildPath)/\(platform.carthageName)"
break
}
return path
}

/// Fetches all carthage dependencies for a given target
Expand Down
11 changes: 8 additions & 3 deletions Sources/XcodeGenKit/CarthageVersionLoader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,13 @@ class CarthageVersionLoader {
struct CarthageVersionFile: Decodable {

private struct Reference: Decodable, Equatable {
public let name: String
public let hash: String
let name: String
let hash: String
let container: String?

var frameworkFilename: String {
container ?? "\(name).framework"
}
}

private let data: [Platform: [String]]
Expand All @@ -67,7 +72,7 @@ struct CarthageVersionFile: Decodable {
let container = try decoder.container(keyedBy: Platform.self)
data = try Platform.allCases.reduce(into: [:]) { data, platform in
let references = try container.decodeIfPresent([Reference].self, forKey: platform) ?? []
let frameworks = Set(references.map { $0.name }).sorted()
let frameworks = Set(references.map { $0.frameworkFilename }).sorted()
data[platform] = frameworks
}
}
Expand Down
60 changes: 33 additions & 27 deletions Sources/XcodeGenKit/PBXProjGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public class PBXProjGenerator {
var packageReferences: [String: XCRemoteSwiftPackageReference] = [:]

var carthageFrameworksByPlatform: [String: Set<PBXFileElement>] = [:]
var carthageXCFrameworks: Set<PBXFileElement> = []
var frameworkFiles: [PBXFileElement] = []
var bundleFiles: [PBXFileElement] = []

Expand Down Expand Up @@ -225,8 +226,8 @@ public class PBXProjGenerator {
try project.targets.forEach(generateTarget)
try project.aggregateTargets.forEach(generateAggregateTarget)

if !carthageFrameworksByPlatform.isEmpty {
var platforms: [PBXGroup] = []
if !carthageFrameworksByPlatform.isEmpty || !carthageXCFrameworks.isEmpty {
var children: [PBXFileElement] = []
for (platform, files) in carthageFrameworksByPlatform {
let platformGroup: PBXGroup = addObject(
PBXGroup(
Expand All @@ -235,11 +236,14 @@ public class PBXProjGenerator {
path: platform
)
)
platforms.append(platformGroup)
children.append(platformGroup)
}
if !carthageXCFrameworks.isEmpty {
children += Array(carthageXCFrameworks)
}
let carthageGroup = addObject(
PBXGroup(
children: platforms,
children: children,
sourceTree: .group,
name: "Carthage",
path: carthageResolver.buildPath
Expand Down Expand Up @@ -677,7 +681,7 @@ public class PBXProjGenerator {

let targetSupportsDirectEmbed = !(target.platform.requiresSimulatorStripping &&
(target.type.isApp || target.type == .watch2Extension))
let directlyEmbedCarthage = target.directlyEmbedCarthageDependencies ?? targetSupportsDirectEmbed
let directlyEmbedCarthage = target.directlyEmbedCarthageDependencies ?? project.options.carthageXCFrameworks || targetSupportsDirectEmbed
Copy link
Collaborator

@freddi-kit freddi-kit May 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

↓ may be correct?

Suggested change
let directlyEmbedCarthage = target.directlyEmbedCarthageDependencies ?? project.options.carthageXCFrameworks || targetSupportsDirectEmbed
let directlyEmbedCarthage = target.directlyEmbedCarthageDependencies ?? (project.options.carthageXCFrameworks || targetSupportsDirectEmbed)

This makes one failed test passing.


func getEmbedSettings(dependency: Dependency, codeSign: Bool) -> [String: Any] {
var embedAttributes: [String] = []
Expand Down Expand Up @@ -871,14 +875,14 @@ public class PBXProjGenerator {
? carthageResolver.relatedDependencies(for: dependency, in: target.platform) : [dependency]
allDependencies.forEach { dependency in

let platformPath = Path(carthageResolver.buildPath(for: target.platform, linkType: linkType))
var frameworkPath = platformPath + dependency.reference
if frameworkPath.extension == nil {
frameworkPath = Path(frameworkPath.string + ".framework")
}
let fileReference = self.sourceGenerator.getFileReference(path: frameworkPath, inPath: platformPath)
let frameworkPath = carthageResolver.frameworkPath(for: dependency.reference, platform: target.platform, linkType: linkType)
let fileReference = self.sourceGenerator.getFileReference(path: frameworkPath, inPath: frameworkPath.parent())

self.carthageFrameworksByPlatform[target.platform.carthageName, default: []].insert(fileReference)
if frameworkPath.extension == "xcframework" {
self.carthageXCFrameworks.insert(fileReference)
} else {
self.carthageFrameworksByPlatform[target.platform.carthageName, default: []].insert(fileReference)
}

let isStaticLibrary = target.type == .staticLibrary
let isCarthageStaticLink = dependency.carthageLinkType == .static
Expand Down Expand Up @@ -954,12 +958,8 @@ public class PBXProjGenerator {
let isFromTopLevelTarget = carthageDependency.isFromTopLevelTarget
let embed = dependency.embed ?? target.shouldEmbedCarthageDependencies

let platformPath = Path(carthageResolver.buildPath(for: target.platform, linkType: dependency.carthageLinkType ?? .default))
var frameworkPath = platformPath + dependency.reference
if frameworkPath.extension == nil {
frameworkPath = Path(frameworkPath.string + ".framework")
}
let fileReference = sourceGenerator.getFileReference(path: frameworkPath, inPath: platformPath)
let frameworkPath = carthageResolver.frameworkPath(for: dependency.reference, platform: target.platform, linkType: dependency.carthageLinkType ?? .default)
let fileReference = self.sourceGenerator.getFileReference(path: frameworkPath, inPath: frameworkPath.parent())

if dependency.carthageLinkType == .static {
guard isFromTopLevelTarget else { continue } // ignore transitive dependencies if static
Expand Down Expand Up @@ -1079,7 +1079,7 @@ public class PBXProjGenerator {
if !carthageFrameworksToEmbed.isEmpty {

let inputPaths = carthageFrameworksToEmbed
.map { "$(SRCROOT)/\(carthageResolver.buildPath(for: target.platform, linkType: .dynamic))/\($0)\($0.contains(".") ? "" : ".framework")" }
.map { "$(SRCROOT)/\(carthageResolver.frameworkPath(for: $0, platform: target.platform, linkType: .dynamic))" }
let outputPaths = carthageFrameworksToEmbed
.map { "$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/\($0)\($0.contains(".") ? "" : ".framework")" }
let carthageExecutable = carthageResolver.executable
Expand Down Expand Up @@ -1249,15 +1249,21 @@ public class PBXProjGenerator {
let configFrameworkBuildPaths: [String]
if !carthageDependencies.isEmpty {
var carthagePlatformBuildPaths: Set<String> = []
if carthageDependencies.contains(where: { $0.dependency.carthageLinkType == .static }) {
let carthagePlatformBuildPath = "$(PROJECT_DIR)/" + carthageResolver.buildPath(for: target.platform, linkType: .static)
carthagePlatformBuildPaths.insert(carthagePlatformBuildPath)
}
if carthageDependencies.contains(where: { $0.dependency.carthageLinkType == .dynamic }) {
let carthagePlatformBuildPath = "$(PROJECT_DIR)/" + carthageResolver.buildPath(for: target.platform, linkType: .dynamic)
carthagePlatformBuildPaths.insert(carthagePlatformBuildPath)
func getCarthageBuildPaths(linkType: Dependency.CarthageLinkType) -> Set<String> {
var paths: Set<String> = []
if carthageDependencies.contains(where: { $0.dependency.carthageLinkType == linkType }) {
let path = "$(PROJECT_DIR)/\(carthageResolver.buildPath(for: target.platform, linkType: linkType, xcFramework: false))"
paths.insert(path)
if project.options.carthageXCFrameworks {
let path = "$(PROJECT_DIR)/\(carthageResolver.buildPath(for: target.platform, linkType: linkType, xcFramework: true))/**"
paths.insert(path)
}
}
return paths
}
configFrameworkBuildPaths = carthagePlatformBuildPaths.sorted() + frameworkBuildPaths.sorted()
carthagePlatformBuildPaths = carthagePlatformBuildPaths.union(getCarthageBuildPaths(linkType: .static))
carthagePlatformBuildPaths = carthagePlatformBuildPaths.union(getCarthageBuildPaths(linkType: .dynamic))
configFrameworkBuildPaths = carthagePlatformBuildPaths.union(frameworkBuildPaths).sorted()
} else {
configFrameworkBuildPaths = frameworkBuildPaths.sorted()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class CarthageDependencyResolverTests: XCTestCase {
}

try allPlatforms.forEach { platform in
try expect(expectedByPlatform[platform]) == resolver.buildPath(for: platform, linkType: .dynamic)
try expect(expectedByPlatform[platform]) == resolver.buildPath(for: platform, linkType: .dynamic, xcFramework: false)
}
}
}
Expand Down