Skip to content
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

[WIP] Catalyst support #3235

Draft
wants to merge 5 commits 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ PREFIX?=/usr/local
INTERNAL_PACKAGE=CarthageApp.pkg
OUTPUT_PACKAGE=Carthage.pkg

CARTHAGE_EXECUTABLE=./.build/release/carthage
CARTHAGE_EXECUTABLE=./.build/debug/carthage
BINARIES_FOLDER=$(PREFIX)/bin

SWIFT_BUILD_FLAGS=--configuration release -Xswiftc -suppress-warnings
SWIFT_BUILD_FLAGS=--configuration debug -Xswiftc -suppress-warnings

SWIFTPM_DISABLE_SANDBOX_SHOULD_BE_FLAGGED:=$(shell test -n "$${HOMEBREW_SDKROOT}" && echo should_be_flagged)
ifeq ($(SWIFTPM_DISABLE_SANDBOX_SHOULD_BE_FLAGGED), should_be_flagged)
Expand Down
20 changes: 19 additions & 1 deletion Source/CarthageKit/BuildSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,20 @@ public struct BuildSettings {
return load(with: BuildArguments(project: project, scheme: scheme))
.zip(with: SDK.setsFromJSONShowSDKsWithFallbacks.promoteError(CarthageError.self))
.take(first: 1)
.map { $1.intersection($0.buildSDKRawNames.map { sdk in SDK(name: sdk, simulatorHeuristic: "") }) }
.map {
$1.intersection($0.buildSDKRawNames.flatMap { sdk -> Set<SDK> in
var result: Set<SDK> = [
SDK(name: sdk, simulatorHeuristic: "")
]

if sdk == "macosx" {
// Add a copy of this SDK for the macCatalyst variant
result.update(with: SDK(name: sdk, simulatorHeuristic: "", variant: .macCatalyst))
}

return result
})
}
.flatten()
}

Expand Down Expand Up @@ -258,6 +271,11 @@ public struct BuildSettings {
public var productName: Result<String, CarthageError> {
return self["PRODUCT_NAME"]
}

/// Attempts to determine the full name of the built product.
public var fullProductName: Result<String, CarthageError> {
return self["FULL_PRODUCT_NAME"]
}

/// Attempts to determine the URL to the built product's wrapper, corresponding
/// to its xcodebuild action.
Expand Down
10 changes: 10 additions & 0 deletions Source/CarthageKit/FrameworkExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,16 @@ extension URL {
return .failure(.readFailed(self, error))
}

public func hasSubdirectory(_ name: String) -> Bool {
let manager = FileManager.default

var isDirectory: ObjCBool = false
let exists = manager.fileExists(atPath: self.appendingPathComponent(name).path,
isDirectory: &isDirectory)

return exists && isDirectory.boolValue
}

public func hasSubdirectory(_ possibleSubdirectory: URL) -> Bool {
let standardizedSelf = self.standardizedFileURL
let standardizedOther = possibleSubdirectory.standardizedFileURL
Expand Down
2 changes: 2 additions & 0 deletions Source/CarthageKit/XCDBLDExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ extension MachOType {
}
}

// TODO: ?

extension SDK {
/// The relative path at which binaries corresponding to this platform will
/// be stored.
Expand Down
404 changes: 218 additions & 186 deletions Source/CarthageKit/Xcode.swift

Large diffs are not rendered by default.

21 changes: 17 additions & 4 deletions Source/XCDBLD/FrameworkBundle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ public func mergeIntoXCFramework(
debugSymbols: [URL],
platformName: String,
variant: String?,
outputURL: URL
outputURL: URL,
isCatalyst: Bool
) -> SignalProducer<URL, TaskError> {
let baseArguments = ["xcodebuild", "-create-xcframework", "-allow-internal-distribution", "-output", outputURL.path]
let newLibraryArguments = ["-framework", framework.path] + debugSymbols.flatMap { ["-debug-symbols", $0.path] }
Expand All @@ -66,9 +67,15 @@ public func mergeIntoXCFramework(
}
.flatMap(.concat) { xcframework -> SignalProducer<XCFramework.Library, AnyError> in
// Only persist frameworks which _won't_ be overwritten by the new library
return SignalProducer(xcframework.availableLibraries.filter { library in
library.supportedPlatform != platformName || library.supportedPlatformVariant != variant
})
return SignalProducer(
xcframework.availableLibraries
.filter { library in
!(library.isCatalyst && isCatalyst)
}
.filter { library in
library.supportedPlatform != platformName || library.supportedPlatformVariant != variant
}
)
}
.flatMap(.concat) { library -> SignalProducer<String, AnyError> in
// Discover and include dSYMs and bcsymbolmaps for each library
Expand Down Expand Up @@ -128,3 +135,9 @@ struct XCFramework: Decodable {
case version = "XCFrameworkFormatVersion"
}
}

extension XCFramework.Library {
var isCatalyst: Bool {
return self.supportedPlatformVariant == "maccatalyst"
}
}
71 changes: 59 additions & 12 deletions Source/XCDBLD/SDK.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,23 @@ import ReactiveSwift
/// `platformSimulatorlessFromHeuristic` which (in practice) usually
/// draws upon data from `xcodebuild -showsdks -json`.
public struct SDK: Hashable {
public enum Variant {
case macCatalyst
}

private let name: String
private let simulatorHeuristic: String
// it’s a fairly solid heuristic

public init(name: String, simulatorHeuristic: String) {
(self.name, self.simulatorHeuristic) = (name, simulatorHeuristic)
private var variant: Variant? = nil
public init(name: String, simulatorHeuristic: String) {
self.init(name: name, simulatorHeuristic: simulatorHeuristic, variant: nil)
}


public init(name: String, simulatorHeuristic: String, variant: Variant?) {
(self.name, self.simulatorHeuristic) = (name, simulatorHeuristic)
self.variant = variant
}

public var rawValue: String { return name.lowercased() }

public var isSimulator: Bool {
Expand All @@ -35,6 +44,10 @@ public struct SDK: Hashable {
public var isDevice: Bool {
return !isSimulator
}

public var isMacCatalyst: Bool {
return self.variant == .macCatalyst
}

public func hash(into: inout Hasher) {
return into.combine(self.rawValue)
Expand Down Expand Up @@ -78,7 +91,7 @@ public struct SDK: Hashable {
/// - Note: The aliases are intended to be matched case-insensitevly.
private static let knownIn2019YearDictionary: [String: (String, [String], String)] =
([
"MacOSX": (["macOS", "Mac", "OSX"], "macOS"),
"MacOSX": (["macOS", "Mac", "OSX", "macCatalyst"], "macOS"),
"iPhoneOS": (["iOS Device", "iOS"], "iOS"),
"iPhoneSimulator": (["iOS Simulator"], "Simulator - iOS"),
"WatchOS": (["watchOS"], "watchOS"),
Expand Down Expand Up @@ -129,7 +142,12 @@ public struct SDK: Hashable {
]
.reduce(into: [] as Set<SDK>) {
guard let value = $1 else { return }
$0.formUnion([SDK(name: value.0, simulatorHeuristic: value.2)])

let variant: Variant? = argumentSubstring == "macCatalyst"
? .macCatalyst
: nil

return $0.formUnion([SDK(name: value.0, simulatorHeuristic: value.2, variant: variant)])
}
}
}
Expand All @@ -146,19 +164,21 @@ extension SDK {
.materializeResults() // to map below and ignore errors
.filterMap { try? JSONSerialization.jsonObject(with: $0.value?.value ?? Data(bytes: []), options: JSONSerialization.ReadingOptions()) as? NSArray ?? NSArray() }
.map {
$0.compactMap { (nsobject: Any) -> SDK? in
$0
.map { $0 as! NSObject }
.compactMap { nsobject -> SDK? in
let platform = NSString.lowercased(
(nsobject as! NSObject).value(forKey: "platform") as? NSString ?? ""
nsobject.value(forKey: "platform") as? NSString ?? ""
)(with: Locale?.none)

guard platform.isEmpty == false else { return nil }

guard NSString.lowercased(
(nsobject as! NSObject).value(forKey: "canonicalName") as? NSString ?? "\0"
nsobject.value(forKey: "canonicalName") as? NSString ?? "\0"
)(with: Locale?.none).hasPrefix(platform) else { return nil }

let simulatorHeuristic = CollectionOfOne(
(nsobject as! NSObject).value(forKey: "displayName") as? NSString
nsobject.value(forKey: "displayName") as? NSString
).reduce(into: "") {
$0 = $1?.appending("") ?? $0
let potentialVersion = $0.reversed().drop(while: "1234567890.".contains)
Expand All @@ -180,7 +200,7 @@ extension SDK {
}

let titleCasedPlatform = repeatElement(
(nsobject as! NSObject).value(forKey: "platformPath") as? NSString ?? "", count: 1
nsobject.value(forKey: "platformPath") as? NSString ?? "", count: 1
).reduce(into: String?.none) { $0 = parseTitleCasePlatform($1.appending("")) }

return SDK(name: titleCasedPlatform ?? platform, simulatorHeuristic: simulatorHeuristic)
Expand All @@ -192,6 +212,33 @@ extension SDK {
}
}

extension SDK {
public var productName: String {
switch variant {
case .none: return self.name
case .macCatalyst: return "\(self.name)-catalyst"
}
}
}

extension SDK {
public var destination: String? {
switch (self.rawValue, self.variant) {
case (_, .macCatalyst?): return "generic/platform=macOS,variant=Mac Catalyst"
case ("macos", _): return "generic/platform=macOS"

Choose a reason for hiding this comment

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

This is causing an error later when building for macOS

CarthageKit/Xcode.swift:1046: Fatal error: Unexpectedly found nil while unwrapping an Optional value

I believe it should be:

Suggested change
case ("macos", _): return "generic/platform=macOS"
case ("macosx", _): return "generic/platform=macOS"

Choose a reason for hiding this comment

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

Works for me otherwise, and solves my issue (see getsentry/sentry-cocoa#2031)

Will be great to see this merged in. Thanks for you hard work!

case ("iphoneos", nil): return "generic/platform=iOS"
case ("iphonesimulator", nil): return "generic/platform=iOS Simulator"
case ("watchos", nil): return "generic/platform=watchOS"
case ("watchsimulator", nil): return "generic/platform=watchOS Simulator"
case ("appletvos", nil): return "generic/platform=tvOS"
case ("appletvsimulator", nil): return "generic/platform=tvOS Simulator"

default:
return nil
}
}
}

extension SDK: CustomStringConvertible {
public var description: String {
return SDK.knownIn2019YearDictionary[self.rawValue]?.1.first!
Expand Down
2 changes: 1 addition & 1 deletion Source/carthage/Build.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ extension BuildOptions: OptionsProtocol {
}

public static func evaluate(_ mode: CommandMode, addendum: String) -> Result<BuildOptions, CommandantError<CarthageError>> {
var platformUsage = "the platforms to build for (one of 'all', 'macOS', 'iOS', 'watchOS', 'tvOS', or comma-separated values of the formers except for 'all')"
var platformUsage = "the platforms to build for (one of 'all', 'macOS', 'iOS', 'watchOS', 'tvOS', 'macCatalyst', or comma-separated values of the formers except for 'all')"
platformUsage += addendum

return curry(BuildOptions.init)
Expand Down
2 changes: 2 additions & 0 deletions Tests/XCDBLDTests/BuildArgumentsSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ class BuildArgumentsSpec: QuickSpec {
itCreatesBuildArguments("includes the destination if given", arguments: ["-destination", "exampleDestination"]) { subject in
subject.destination = "exampleDestination"
}

// TODO: variant

describe("specifying onlyActiveArchitecture") {
itCreatesBuildArguments("includes ONLY_ACTIVE_ARCH=YES if it's set to true", arguments: ["ONLY_ACTIVE_ARCH=YES"]) { subject in
Expand Down
1 change: 1 addition & 0 deletions Tests/XCDBLDTests/SDKSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class SDKEncompassingPlatformsSpec: QuickSpec {
SDK(name: "platformbox", simulatorHeuristic: ""): (false, "platformbox"),
SDK(name: "wAtchsiMulator", simulatorHeuristic: ""): (true, "watchOS"),
SDK(name: "macosx", simulatorHeuristic: ""): (false, "Mac"), /* special case */
SDK(name: "macCatalyst", simulatorHeuristic: ""): (false, "Mac"), /* special case */
]

pairs.forEach { sdk, result in
Expand Down