Skip to content

Investigate outputting App.framework as xcframework #36204

Closed

Description

@jmagman

Xcode 11 introduces a new bundle format .xcframework for distributing frameworks for multiple platforms and architectures. At build time, the correct version of the framework is linked on.

Creating an App.xcframework may decrease iOS and macOS build time by cutting out the lipo stages, since App.xcframework could be built with all supported architectures and not one fat binary.

Activity

  1. added
    platform-iosiOS applications specifically
    toolAffects the "flutter" command-line tool. See also t: labels.
    t: xcode"xcodebuild" on iOS and general Xcode project management
    on Jul 15, 2019
  2. jmagman commented on Jul 15, 2019

    @jmagman
    MemberAuthor

    XCFramework bundle layout

    I made a xcframework to see what gets put where in the bundle. I archived a few frameworks from macOS, iOS, iOS simulator, and tvOS.

    $ xcodebuild -create-xcframework -framework Debug/DistributedFramework.framework -framework Debug-appletvos/DistributedFramework.framework -framework Debug-iphoneos/DistributedFramework.framework -framework Debug-iphonesimulator/DistributedFramework.framework -output out1.xcframework
    
    out1.xcframework
    ├── Info.plist
    ├── ios-arm64/
    │   ├── DistributedFramework.framework
    ├── ios-x86_64-simulator/
    │   ├── DistributedFramework.framework
    ├── macos-x86_64/
    │   ├── DistributedFramework.framework
    ├── tvos-arm64/
    │   ├── DistributedFramework.framework
    

    Here's the Info.plist (note SupportedArchitectures and SupportedPlatformVariant keys):
    Info.plist.txt

    $ plutil -p Info.plist
    {
    "AvailableLibraries" => [
    0 => {
    "LibraryIdentifier" => "ios-x86_64-simulator"
    "LibraryPath" => "DistributedFramework.framework"
    "SupportedArchitectures" => [
    0 => "x86_64"
    ]
    "SupportedPlatform" => "ios"
    "SupportedPlatformVariant" => "simulator"
    }
    1 => {
    "LibraryIdentifier" => "ios-arm64"
    "LibraryPath" => "DistributedFramework.framework"
    "SupportedArchitectures" => [
    0 => "arm64"
    ]
    "SupportedPlatform" => "ios"
    }
    2 => {
    "LibraryIdentifier" => "macos-x86_64"
    "LibraryPath" => "DistributedFramework.framework"
    "SupportedArchitectures" => [
    0 => "x86_64"
    ]
    "SupportedPlatform" => "macos"
    }
    3 => {
    "LibraryIdentifier" => "tvos-arm64"
    "LibraryPath" => "DistributedFramework.framework"
    "SupportedArchitectures" => [
    0 => "arm64"
    ]
    "SupportedPlatform" => "tvos"
    }
    ]
    "CFBundlePackageType" => "XFWK"
    "XCFrameworkFormatVersion" => "1.0"
    }

    Build configurations

    There doesn't seem to be support for build configuration since this feature is really for distribution, so it's expected to come from an Archive (which is Release by default). So we may need:
    App.xcframework
    App-profile.xcframework
    App-release.xcframework
    ?
    Which means different linker flags per build configuration.

    Impact

    • Could remove xcode_backend.sh thin.
    • Could simplify build_aot.dart, mac.dart. Find usages of getNameForIOSArch and calls to lipo to see where the code would need to change.
      String getNameForIOSArch(IOSArch arch) {
    • Only works in Xcode 11 (likely to be released September 2019)
  3. jmagman commented on Jul 15, 2019

    @jmagman
    MemberAuthor

    @xster - add-to-app implications for easier linking in host app
    @cbracken - I think he added the first version of app thinning
    @stuartmorgan - impact to flutterMacOSFramework code paths
    @jonahwilliams - flutter assemble work

  4. jmagman commented on Jul 15, 2019

    @jmagman
    MemberAuthor

    The frameworks in the architecture directories looks like a normal framework. So "only works in Xcode 11" means that the .xcframework can only be linked directly in Xcode 11. However once the .xcframework is created, versions of Xcode earlier than 11 can still link on the App.framework in the right arch directory, which still cuts out the lipo. But flutter tools would need to figure out those directories instead of letting Xcode handle it at build time.

  5. xster commented on Jul 15, 2019

    @xster
    Member

    I assume the xcframework can also only be generated on machines with xcode 11 right? In other words, in a flutter build framework command, we might still need to produce 2 paths to support machines on xcode 10- that invoke that command?

  6. jmagman commented on Jul 15, 2019

    @jmagman
    MemberAuthor

    I assume the xcframework can also only be generated on machines with xcode 11 right? In other words, in a flutter build framework command, we might still need to produce 2 paths to support machines on xcode 10- that invoke that command?

    Yes, xcodebuild -create-xcframework only works on Xcode 11.

    1. When App.framework isn't version controlled (is that binary ever?), flutter build aot/xcode_backend.sh/whatever flutter assemble becomes can decide which bundle to build and link based on the locally installed version of Xcode, right?

    2. The xcframework bundle looks pretty simple. That xcodebuild command could be skipped if flutter tools signed up for copying the .framework files to the right directories and generating that Info.plist. Xcode 10 could link on the .framework, and Xcode 11 could link on the xcframework.
      The flutter tooling already knows the bundle structure on iOS (example Flutter.framework as a dylib with headers copied to a Headers directory, or copying files to /Versions/A/) so that bridge is crossed.

    Add-to-app tooling might need to handle both types of bundles?

  7. xster commented on Jul 15, 2019

    @xster
    Member

    Ah ok, so we're still saying there are 2 paths.

    A- If both supplier and consumer have xcode 11, you just invoke flutter build framework, no arguments, then in your outer project, declare dependency onto the xcframework. Then everything's driven by your outer project's supported architectures.
    B- If either doesn't have xcode 11, you do some manual steps. i.e. if the supplier doesn't have, you need to pass the architecture to the flutter build framework command so it lipos the the right thing. If the consumer doesn't have it, it needs to go depend on the .framework subdirectory in the .xcframework directory depending on what architecture they want in the outer project.

    Is that right? i.e. we can't really delete xcode_backend.sh thin

  8. jmagman commented on Jul 15, 2019

    @jmagman
    MemberAuthor

    A- If both supplier and consumer have xcode 11, you just invoke flutter build framework, no arguments, then in your outer project, declare dependency onto the xcframework. Then everything's driven by your outer project's supported architectures.

    Yes, Xcode would handle linking the right App.framework architecture based on the outer project's architecture.

    B- If either doesn't have xcode 11, you do some manual steps. i.e. if the supplier doesn't have, you need to pass the architecture to the flutter build framework command so it lipos the the right thing. If the consumer doesn't have it, it needs to go depend on the .framework subdirectory in the .xcframework directory depending on what architecture they want in the outer project.

    Is that right? i.e. we can't really delete xcode_backend.sh thin

    We could go two ways:

    1. Stop making fat universal binaries and instead manually make a xcframework with the right architecture directories. flutter tools would be on the hook for setting up the linker flags to point at the right App.framework architecture based on the outer app's target architecture. Then thin could go away.
    2. Have two code paths, as you say, and keep thin (less risky).

    BUT:
    When I moved beyond the barest proof of concept and looked at how build_aot creates the framework in release mode, it doesn't even work!

    xcodebuild -create-xcframework -framework build/aot/armv7/App.framework -framework build/aot/arm64/App.framework -output build/aot/App-release.xcframework                                                                                                                                                                                  
    Both ios-arm64 and ios-armv7 represent two equivalent library definitions.
    

    Maybe they didn't bother supporting ios-armv7 since it was last used in the iPhone 4S/iPad 2 A5 chip (2011 generation)? Just guessing since there's no official documentation for this feature...

  9. xster commented on Jul 15, 2019

    @xster
    Member

    Oh ok, got it. You're saying the supplier doesn't need xcode 11 since we can manually package .frameworks up into the .xcframework vehicle. (kinda like what this guy is doing manually https://github.com/jeffctown/xcframework)

    If we can produce an example of how to consume it without xcode 11, this SGTM.

    Is the problem specifically around 32bit arm? i.e. if you did that exercise with x64 and 64bit arm it works?

  10. jmagman commented on Jul 15, 2019

    @jmagman
    MemberAuthor

    It seems to think that arm64, arm64e, armv7, and arm7s (default VALID_ARCHS) all represent "equivalent library definitions" and I get the same error, though the Mach-O runtime architectures are different:

    $ file armvs/DistributedFramework.framework/DistributedFramework 
    armv7s/DistributedFramework.framework/DistributedFramework: Mach-O dynamically linked shared library arm_v7s
    $ file armv7/DistributedFramework.framework/DistributedFramework 
    armv7/DistributedFramework.framework/DistributedFramework: Mach-O dynamically linked shared library arm_v7
    $ file arm64e/DistributedFramework.framework/DistributedFramework 
    arm64e/DistributedFramework.framework/DistributedFramework: Mach-O 64-bit dynamically linked shared library arm64
    

    For reference when all VALID_ARCHS are selected, I only see armv7 and arm64 (I don't know enough about this topic to tell you why):

    $ file DistributedFramework 
    DistributedFramework: Mach-O universal binary with 2 architectures: [arm_v7:Mach-O dynamically linked shared library arm_v7] [arm64:Mach-O 64-bit dynamically linked shared library arm64]
    DistributedFramework (for architecture armv7):	Mach-O dynamically linked shared library arm_v7
    DistributedFramework (for architecture arm64):	Mach-O 64-bit dynamically linked shared library arm64
    

    This feature really seems to be about platforms and variants (simulator) rather than architecture.
    So it's still interesting for swapping between real devices and simulators, or distributing iOS and macOS and UIKit for Mac the same way if we also investigated for Flutter.xcframework, but it doesn't look like it's going to get the tools off app framework thinning.

  11. xster commented on Jul 16, 2019

    @xster
    Member

    I didn't watch the whole thing but looking through the slides at https://developer.apple.com/videos/play/wwdc2019/416, they never explicitly talked about architectures. Just mixing swift/objc, uikit/appkit etc. Are the iOS-UIKit-objc framework part of a xcframework still a fat binary? Maybe it's trying to address issues at a different granularity than architectures.

  12. jmagman commented on Jul 16, 2019

    @jmagman
    MemberAuthor

    Are the iOS-UIKit-objc framework part of a xcframework still a fat binary? Maybe it's trying to address issues at a different granularity than architectures.

    Seems to be. I had hopes because the subdirectories in my prototype looked like:

    ├── ios-arm64/
    ├── ios-x86_64-simulator/
    

    etc. and the Info.plist contained lists of SupportedArchitectures. I was hoping to get

    ├── ios-armv7/
    ├── ios-arm64/
    ├── ios-x86_64-simulator/
    

    but it doesn't seem to split it up beyond arm64.

  13. jonahwilliams commented on Jul 16, 2019

    @jonahwilliams
    Contributor

    Creating an App.xcframework may decrease iOS and macOS build time by cutting out the lipo stages

    How much time would cutting out the lipo save? We could also accomplish something similar by moving the lipo call into part of the targets for the iOS flutter assemble implementation. Then it will be skipped by the flutter_tool/xcode if the architectures/inputs haven't changed

  14. jmagman commented on Feb 8, 2020

    @jmagman
    MemberAuthor

    Minimum version was just updated to Xcode 11: #50371

  15. added
    P3Issues that are less important to the Flutter project
    on Aug 11, 2020
  16. jmagman commented on Nov 14, 2020

    @jmagman
    MemberAuthor

    Closing this. App.frameworks are not distributed binaries.

  17. alexviquez commented on May 1, 2021

    @alexviquez

    Hi @jmagman i have a question about this closed issue, apple actually doesn't support create archives in lower versions than Xcode 12, i have older version of flutter (v1.7.8+hotfix.4) after usage of XCFrameworks, that means my build can't find armv7 architecture having this issue

    lipo: -extract armv7 specified but fat file: /Users/xyz/Library/Developer/Xcode/DerivedData/Runner-dadfkcamaimtsnhgjdoojqnyuztv/Build/Intermediates.noindex/ArchiveIntermediates/Runner/InstallationBuildProductsLocation/Applications//Runner.app/Frameworks/App.framework/App does not contain that architecture
    

    there is a posibility to fix this issue in older version of flutter or is necessary upgrade flutter?

  18. jmagman commented on May 4, 2021

    @jmagman
    MemberAuthor

    @alexviquez we don't hotfix old versions of Flutter. However, you can fork Flutter and add your own patches to the branch you're on.
    https://github.com/flutter/flutter/wiki/Flutter-build-release-channels#will-a-particular-bug-fix-be-provided-in-a-hotfix-release

    I recommend upgrading, though (if there's a bug preventing you from upgrading, please file it!)

  19. github-actions commented on Aug 2, 2021

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  20. locked as resolved and limited conversation to collaborators on Aug 2, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Issues that are less important to the Flutter projectc: contributor-productivityTeam-specific productivity, code health, technical debt.platform-iosiOS applications specificallyt: xcode"xcodebuild" on iOS and general Xcode project managementtoolAffects the "flutter" command-line tool. See also t: labels.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions