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

Xcframework prebuilt binaries #3123

Merged

Conversation

igstewart3
Copy link

This PR adds the ability to retrieve XCFramework prebuilt binaries. It installs them into the Carthage/Build folder and skips any Xcode.swift steps that involve checking the architecture.

It could probably use a wee bit of guidance from someone more familiar with the project to ensure I haven't missed something important.

I have tested this on a local project and was successfully able to install XCFramework binaries, as well as regular Framework binaries from a previous release.

Note: I have not been able to run the unit tests yet due to some Swift compile errors I haven't been able to resolve.

@sebskuse
Copy link

sebskuse commented Feb 9, 2021

I've tested this against the latest https://github.com/aws-amplify/aws-sdk-ios, which provides prebuilt XCFramework binaries and this works as expected!

Using Carthage 0.37.0 we see:

*** Fetching aws-sdk-ios
*** Checking out aws-sdk-ios at "2.22.2"
*** No cache found for aws-sdk-ios, building with all downstream dependencies
*** xcodebuild output can be found in /var/folders/c0/068z__x16p15190541prsvkm0000gp/T/carthage-xcodebuild.RnWKmY.log
*** Downloading aws-sdk-ios.framework binary at "AWS SDK for iOS 2.22.2"
***  Skipped installing aws-sdk-ios.framework binary due to the error:
	"Invalid archive - Found multiple frameworks with the same unarchiving destination:
* 	file:///var/folders/c0/068z__x16p15190541prsvkm0000gp/T/carthage-archive.IXODoQ/AWSLogs.xcframework/ios-arm64_i386_x86_64-simulator/AWSLogs.framework/
	file:///var/folders/c0/068z__x16p15190541prsvkm0000gp/T/carthage-archive.IXODoQ/AWSLogs.xcframework/ios-arm64_armv7/AWSLogs.framework/ 
		to:
	file:///Path/To/Carthage/Build/iOS/AWSLogs.framework/

... repeat for every framework

Falling back to building from the source

Using this branch we see:

*** Fetching aws-sdk-ios
*** Checking out aws-sdk-ios at "2.22.2"
*** No cache found for aws-sdk-ios, building with all downstream dependencies
*** xcodebuild output can be found in /var/folders/c0/068z__x16p15190541prsvkm0000gp/T/carthage-xcodebuild.ll5AoF.log
*** Downloading aws-sdk-ios.framework binary at "AWS SDK for iOS 2.22.2"

And the prebuilt frameworks are in the expected folder.

@dmiluski
Copy link

dmiluski commented Feb 10, 2021

Hi @igstewart3 ,

I just ran into this today with a similar hiccup with AWS (wowza HUGE install times). Thanks for kicking off the effort.

I'm not a repo owner/maintainer, but wanted to give some notes as I just started dabbling with this too:

Reading through I noticed this used string literals and bool logic to concatenate framework download behaviors. Skimming the implementation, I noticed:

/// Describes the type of product built by an Xcode target.
public enum ProductType: String {
	/// A framework bundle.
	case framework = "com.apple.product-type.framework"

	/// A static library.
	case staticLibrary = "com.apple.product-type.library.static"

	/// A unit test bundle.
	case testBundle = "com.apple.product-type.bundle.unit-test"

And was wondering if perhaps xcframework should be included as a sibling here? Allowing for the use of this definition for path? Allowing for a model to drive potential differentiation rather than bool checks?

/// Framework Suffix
public enum FrameworkSuffix: String {

    /// Dynamic Framework
    case dynamic = "framework"

    /// XCFramework
    case xcode = "xcframework"
}

@igstewart3
Copy link
Author

Hi @igstewart3 ,

I just ran into this today with a similar hiccup with AWS (wowza HUGE install times). Thanks for kicking off the effort.

I'm not a repo owner/maintainer, but wanted to give some notes as I just started dabbling with this too:

Reading through I noticed this used string literals and bool logic to concatenate framework download behaviors. Skimming the implementation, I noticed:

/// Describes the type of product built by an Xcode target.
public enum ProductType: String {
	/// A framework bundle.
	case framework = "com.apple.product-type.framework"

	/// A static library.
	case staticLibrary = "com.apple.product-type.library.static"

	/// A unit test bundle.
	case testBundle = "com.apple.product-type.bundle.unit-test"

And was wondering if perhaps xcframework should be included as a sibling here? Allowing for the use of this definition for path? Allowing for a model to drive potential differentiation rather than bool checks?

/// Framework Suffix
public enum FrameworkSuffix: String {

    /// Dynamic Framework
    case dynamic = "framework"

    /// XCFramework
    case xcode = "xcframework"
}

Good shout, I'll take a look at tidying that up 👍

@finik
Copy link

finik commented Feb 23, 2021

I can confirm as well, the fix in this branch works, please merge and release this ASAP, this blocks some frameworks from being properly distributed. Thanks

@@ -1447,11 +1457,13 @@ func platformForFramework(_ frameworkURL: URL) -> SignalProducer<SDK, CarthageEr
/// Sends the URL to each framework bundle found in the given directory.
internal func frameworksInDirectory(_ directoryURL: URL) -> SignalProducer<URL, CarthageError> {
return filesInDirectory(directoryURL, kUTTypeFramework as String)
.concat(filesInDirectory(directoryURL, "com.apple.xcframework"))
Copy link

Choose a reason for hiding this comment

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

indentation fix needed.

Copy link
Author

Choose a reason for hiding this comment

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

Done 👍

@@ -1413,6 +1419,9 @@ public func nonDestructivelyStripArchitectures(_ frameworkURL: URL, _ architectu

/// Strips the given architectures from a framework.
private func stripArchitectures(_ packageURL: URL, _ architectures: Set<String>) -> SignalProducer<(), CarthageError> {
guard isNotXCFramework(packageURL) else {
Copy link

Choose a reason for hiding this comment

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

indentation fix needed. Please check the indentation of return statement below it.

Copy link
Author

Choose a reason for hiding this comment

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

Done 👍

@@ -1431,6 +1440,9 @@ private func stripArchitectures(_ packageURL: URL, _ architectures: Set<String>)

// Returns a signal of all architectures present in a given package.
public func architecturesInPackage(_ packageURL: URL, xcrunQuery: [String] = ["lipo", "-info"]) -> SignalProducer<[String], CarthageError> {
guard isNotXCFramework(packageURL) else {
Copy link

Choose a reason for hiding this comment

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

indentation fix needed. Please check the indentation of return statement below it.

Copy link
Author

Choose a reason for hiding this comment

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

Done for all changes made in the PR 👍

Copy link
Contributor

@elliottwilliams elliottwilliams left a comment

Choose a reason for hiding this comment

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

First off, thanks so much for your contribution! I think this makes a lot of sense and is obviously something we need to support.

Aside from the suggestions below, we need to handle checkFrameworkCompatibility correctly. Since it's possible to build an xcframework with -allow-internal-distribution, it's possible to have swiftmodules in the xcframework that aren't compatible with the machine they're downloaded to. This patch should fix it, LMK if there's anything I can clarify:
carthage-3123-additions.patch.txt

Source/CarthageKit/Project.swift Outdated Show resolved Hide resolved
Comment on lines 1670 to 1673

private func isNotXCFramework(_ frameworkUrl: URL) -> Bool {
return FrameworkSuffix.from(string: frameworkUrl.pathExtension).value != .xcframework
}
Copy link
Contributor

Choose a reason for hiding this comment

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

What is this (and all its call sites) doing? AFAICT this is related to copy-frameworks, not downloading binaries?

Copy link
Author

Choose a reason for hiding this comment

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

Several of these methods are called when installing the prebuilt XCFrameworks and fail due to not being able to find the executableURL in the bundle in binaryURL(packageUrl:). We could look into adding a custom case for XCFrameworks like is already in place for .dSYM and (and changing to return an array of URLs), but many of these processes (stripping architectures and debug symbols) don't necessarily make sense for an XCFramework anyway.

Source/CarthageKit/Project.swift Outdated Show resolved Hide resolved
Source/CarthageKit/Project.swift Outdated Show resolved Hide resolved
@elliottwilliams elliottwilliams self-assigned this Feb 28, 2021
@igstewart3
Copy link
Author

@elliottwilliams Thanks very much for the feedback! I've implemented most of it, including the patch you provided. I think only the XCFramework guard cases in Xcode.swift still need to be resolved. They definitely allow the prebuilt binary to be installed successfully but they do feel a little clumsy, keen to hear your thoughts on that.

@elliottwilliams
Copy link
Contributor

@elliottwilliams Thanks very much for the feedback! I've implemented most of it, including the patch you provided. I think only the XCFramework guard cases in Xcode.swift still need to be resolved. They definitely allow the prebuilt binary to be installed successfully but they do feel a little clumsy, keen to hear your thoughts on that.

@igstewart3 I think I got it working – we just need to avoid calling into the dSYM/bcsymbolmap logic higher up. This is similar to what we do on the build side, where we conditionally avoid calling any debug symbols merging code when making an xcframework. See the commit I just pushed :)

I confirmed this works with a github "aws-amplify/aws-sdk-ios" Cartfile, but LMK if there's anything else you're noticing.

@igstewart3
Copy link
Author

@elliottwilliams Nice! Good fix, that looks a lot tidier. Everything seems to be working in my tests as well 👍

Copy link
Contributor

@elliottwilliams elliottwilliams left a comment

Choose a reason for hiding this comment

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

Sounds good! Going to give the other maintainers a moment to review if they'd like.

ncreated added a commit to DataDog/dd-sdk-ios that referenced this pull request Mar 19, 2021
… update --use-xcframeworks`

when installing `dd-sdk-ios` in other app. We need this as `arm64` is disabled in PLCrashReporter
simulator builds, so we cannot produce `DatadogCrashReporting.xcframework` as it is not able to
link `CrashReporter` to our `ios-arm64_x86_64-simulator` slice. This change makes
our SDK produce smaller slice, which is enough for running on simulator.

This was discovered as Carthage doesn’t yet support pre-build xcframeworks:
Carthage/Carthage#3123
ncreated added a commit to DataDog/dd-sdk-ios that referenced this pull request Mar 19, 2021
… update --use-xcframeworks`

when installing `dd-sdk-ios` in other app. We need this as `arm64` is disabled in PLCrashReporter
simulator builds, so we cannot produce `DatadogCrashReporting.xcframework` as it is not able to
link `CrashReporter` to our `ios-arm64_x86_64-simulator` slice. This change makes
our SDK produce smaller slice, which is enough for running on simulator.

This was discovered as Carthage doesn’t yet support pre-build xcframeworks:
Carthage/Carthage#3123
@elliottwilliams elliottwilliams merged commit 5347dee into Carthage:master Mar 19, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants