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

Add Xcode 12 compatible support for debug symbols in XCFrameworks #10111

Closed
johntmcintosh opened this issue Oct 1, 2020 · 16 comments
Closed
Labels
help wanted Help from new or existing contributors would be greatly appreciated! r:xcframeworks Related to the support for XCFrameworks
Milestone

Comments

@johntmcintosh
Copy link
Contributor

johntmcintosh commented Oct 1, 2020

Report

With Xcode 11, there was no automated mechanism for Xcode to embed debug symbols (dSYMs and BCSymbolMaps) that were embedded in an XCFramework into the built product's archive. As a result, CocoaPods' support for XCFrameworks included automatically extracting debug symbols from each architecture's framework directory, and including them into the built app. The expectation was that the XCFramework would be structured like this:

MySDK.dSYMs/
- MySDK.framework.ios-arm64.dSYM
- MySDK.framework.ios-x86_64-simulator.dSYM
MySDK.xcframework/
- Info.plist
- ios-arm64/
    - MySDK.framework/
        - BCSymbolMaps/
- ios-x86_64-simulator/
    - MySDK.framework/

EDIT (Oct 7): Originally I had described that the dSYMs were placing as siblings of the BCSymbolMaps in the Xcode 11 style, but after some additional testing, I realized that the Xcode 11 style that CocoaPods was expecting was for the dSYMs to be placed in a .dSYMs directory as a top level sibling of the .xcframework.

This was great!

Now, Xcode 12 has added support to xcodebuild -create-xcframework for passing a -debug-symbols argument. When creating an XCFramework this way with Xcode 12, the resulting directory structure does not match what we had presumed when initially building support for Xcode 11:

MySDK.xcframework
- Info.plist
- ios-arm64/
    - BCSymbolMaps/
    - dSYMs/
    - MySDK.framework/
- ios-arm64_x86_64-simulator/
    - dSYMs/
    - MySDK.framework/

The debug symbols are placed as siblings of each architecture's .framework rather than as children of it.

As a result, when integrating through CocoaPods, the debug symbols from this type of XCFramework are not being included in the final product archive.

What did you expect to happen?

XCFrameworks that are compiled with Xcode 12 to include debug symbols should automatically support those debug symbols being included in the built app when integrating the XCFramework through CocoaPods.

What happened instead?

XCFrameworks that are compiled with Xcode 12 to include debug symbols automatically support debug symbol inclusion in the built app when manually added to the parent project, but not when integrated through CocoaPods.

CocoaPods Environment

Stack

   CocoaPods : 1.10.0.rc.1
        Ruby : ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-darwin19]
    RubyGems : 3.0.3
        Host : Mac OS X 10.15.5 (19F101)
       Xcode : 11.7 (11E801a)
         Git : git version 2.24.3 (Apple Git-128)
Ruby lib dir : /Users/jtm/.rubies/ruby-2.6.5/lib
Repositories : master - git - https://github.com/CocoaPods/Specs.git @ e77184542ea8a2d527a9c094a82fdd5e6fff1642

               trunk - CDN - https://cdn.cocoapods.org/

Project that demonstrates the issue

I do not have an accessible sample project right now, but wanted to go ahead and create the ticket for visibility as it would be fantastic if this support were able to be added as part of the upcoming 1.10.0 release that already includes several other XCFramework improvements.

@dnkoutso
Copy link
Contributor

dnkoutso commented Oct 1, 2020

I did not implement XCFrameworks so I forget...do we manualloy copy those and the issue here is that we are missing some?

@dnkoutso
Copy link
Contributor

dnkoutso commented Oct 1, 2020

Thanks for the great report, yes a sample app is always helpful.

@johntmcintosh
Copy link
Contributor Author

@dnkoutso correct, the symbols are manually copied and it looks like the relevant scripts (described below) are only looking inside of each architecture's .framework.

Background

To add some more background, my understanding from reading up on some of the original work (I think from the PSPDFKit team) and from @steipete's Twitter is that with Xcode 11 Apple had not provided much guidance on how debug symbols should be handled with XCFrameworks. As a result, I think that the current approach of placing them inside the .frameworks may have been community convention more than anything else.

Proposal

Going forward, I assume that the ideal case may be for CocoaPods to support pulling in the debug symbols from both locations (sibling or child of the .framework) to maintain support for the current community convention while also supporting compatibility with the drag-and-drop style now supported with Xcode 12.

Implementation

On the implementation side, from the tracing I've done so far I'm seeing that after running pod install the generated Pods/Target Support Files/MySDK/MySDK-xcframeworks.sh script concludes with the following lines:

install_xcframework "${PODS_ROOT}/MySDK/MySDK.xcframework" "MySDK" "framework" "ios-x86_64-simulator/MySDK.framework" "ios-arm64/MySDK.framework"

The install_xcframework function calls into select_slice to get the relevant slices which are then copied into $PODS_XCFRAMEWORKS_BUILD_DIR. I believe that because the last two input arguments to install_xcframework have the /MySDK.framework suffix, that the result of this script is that only the relevant .frameworks are copied into $PODS_XCFRAMEWORKS_BUILD_DIR, rather than the relevant frameworks and their debug symbol siblings.

Continuing to trace what I think is happening... there's also a generated Pods/Target Support Files/Pods-MyApp/Pods-MyApp-frameworks.sh script which is responsible for taking things from the $PODS_XCFRAMEWORKS_BUILD_DIR and putting them into the final product. That script currently copies dSYMs and BCSymbolMaps from the .framework and then strips them out so a duplicate copy is not leftover inside the final embedded .framework.

I think then that what would need to happen to get this working is:

  1. Update the generated MySDK-xcframeworks.sh script to copy everything in the architecture's directory into $PODS_XCFRAMEWORKS_BUILD_DIR rather than only copying <architecture>/MySDK.framework.
  2. Update the generated Pods-MyApp-frameworks.sh script to copy dSYMs and BCSymbolMaps into the built product from the new, sibling location in $PODS_XCFRAMEWORKS_BUILD_DIR, while still keeping the existing support for copying/stripping them from the child location.

Tomorrow, I can put together a couple of small XCFrameworks as local pods that are setup with a sample Podfile and project so we can have one example that covers both use-cases.

@dnkoutso
Copy link
Contributor

dnkoutso commented Oct 1, 2020

This is great. We can even add an example project for this to ensure it fails without any changes and then starts working with your changes.

@dnkoutso dnkoutso closed this as completed Oct 1, 2020
@dnkoutso dnkoutso reopened this Oct 1, 2020
@dnkoutso
Copy link
Contributor

dnkoutso commented Oct 1, 2020

accidental close, sorry!

@amorde
Copy link
Member

amorde commented Oct 1, 2020

Great write up! Your proposed solution sounds great, especially considering we'd like to continue the existing solution for Xcode 11 at least until Xcode 13 / end of 2021. I'd be happy to review PRs towards this, but I likely won't be working on this myself in the near future

@amorde amorde added the r:xcframeworks Related to the support for XCFrameworks label Oct 1, 2020
@dnkoutso dnkoutso added this to the 1.10.0 milestone Oct 2, 2020
@dnkoutso dnkoutso added the help wanted Help from new or existing contributors would be greatly appreciated! label Oct 2, 2020
@dnkoutso
Copy link
Contributor

dnkoutso commented Oct 2, 2020

@johntmcintosh feel free to try! We can help you land this.

@johntmcintosh
Copy link
Contributor Author

Thanks @dnkoutso -- I have a standalone sample project setup now with two test xcframeworks (one of each style) and a script to compile the wrapper app and evaluate whether the expected symbols are included or not. I think this gets me to a good point now for manually editing the generated scripts until they're doing what I want, and then work backwards into the tool to get CocoaPods to generate the expected scripts on a pod install.

I've run into some trouble getting Rainforest setup and could probably use some pointers if you have any ideas: CocoaPods/Rainforest#88.

@dnkoutso
Copy link
Contributor

dnkoutso commented Oct 2, 2020

@johntmcintosh no need to use Rainforest, just fork this repo normally and cocoapods-integration specs repo and make a PR from that.

@johntmcintosh
Copy link
Contributor Author

@dnkoutso good news -- I've identified a change in the generated scripts (smaller than I originally expected!) that appears to get this working in my example, so now I'm starting to look more seriously into how to test some things out building into the tool locally.

I've gotten setup where I can bundle exec rake spec:integration to run the tests and I've found the existing install_vendored_xcframework test that's probably a good starting point for the new tests that we need.

A couple of questions regarding project conventions and such:

  1. Does it make sense to update the existing test to be something like install_vendored_xcframework_xc11 and add debug symbols to it using the existing Xcode 11 convention, and then setup a new install_vendored_xcframework test (using the original name) that uses the new Xcode 12 convention? (I'm thinking to have the more current name without suffix, and then add the suffix for the previous which is now a special case.)
  2. This test depends on CoconutLib, so we'll want a version of CoconutLib with debug symbols in the Xcode 11 convention and a version with debug symbols in the Xcode 12 convention. Similar question... does it make sense to have a CoconutLib (for Xcode 12 convention) and a CoconutLibXc11 (or something like that) for Xcode 11?
  3. I see that the install_vendored_xcframework test includes a copy of CoconutLib.xcframework, which is used in a few other tests as well, so I'm looking for where to make changes to how that's generated. I've found examples/Vendored XCFramework Example which includes instructions in its Podfile for how to regenerate the xcframework. If I make changes there, are they automatically copied into all places that utilize CoconutLib, or do I copy them manually?

Thanks!

@johntmcintosh
Copy link
Contributor Author

When sitting down this morning to get back into this, it hit me that that the distinction that I was looking to test (differing input library structures) wasn't really a valid use-case for the integration tests, since those are testing the result of a pod install more than they are testing the result of building the resulting project.

I've now updated all of the relevant test cases to pass and reflect the change that I'm proposing. The missing piece still may be adding a new test or example that ensures that the core change of the generated script is actually the right change to be making.

I think I'm at a reasonable point to put up a PR though, and we can sort out any additional test cases like that in the PR.

johntmcintosh added a commit to johntmcintosh/CocoaPods that referenced this issue Oct 5, 2020
…mbols placed as siblings of the .framework inside each arhictecture's directory
johntmcintosh added a commit to johntmcintosh/CocoaPods that referenced this issue Oct 5, 2020
… debug symbols placed as siblings of the .framework inside each architecture's directory
johntmcintosh added a commit to johntmcintosh/CocoaPods that referenced this issue Oct 5, 2020
… debug symbols placed as siblings of the .framework inside each architecture's directory
@amorde
Copy link
Member

amorde commented Oct 5, 2020

The examples are built in CI and may be a more appropriate place to test these integrations. We don't currently have separation of Xcode versions in those but we could.

The place that handles building the examples is here:

CocoaPods/Rakefile

Lines 282 to 285 in a3632cc

desc 'Build all examples'
task :build do
Bundler.require 'xcodeproj', :development
Dir['examples/*'].sort.each do |dir|

Definitely not a blocker for landing these PRs though, we can make incremental progress here

@johntmcintosh
Copy link
Contributor Author

Thanks @amorde! I actually found those today a little while after getting the PRs up. Most of the current examples there do a standard build on the target and I think that the test passing/failing just depends on the target successfully compiling, is that correct?

For this use-case I think that the evaluation we'd want to do of debug symbols requires an archive build to be done. (At least in my testing I've been doing an archive since the symbols are placed at the root directory of the final archive. It may be that they are available at a different path for regular builds and if so a regular build might be able to be used more simply.)

Assuming that understanding is correct, I can look into creating an aggregate target in the Vendored XCFramework example and then have a run script that does an archive and exits with a failing return code if the expected symbols are not present in the built archive. I'm assuming that we would need to do something like that so that the general loop that runs xcodebuild on all schemes would still support this specific test. Let me know if you have any other/better ideas on how would be best to go about setting that up.

@dnkoutso
Copy link
Contributor

@johntmcintosh should we close this now that the PR landed? I am planning to ship 1.10 very soon.

@johntmcintosh
Copy link
Contributor Author

@dnkoutso I had been leaving it open while the PR that adds the example test was still open, but functionally this issue is resolved now with the original PR merged. Feel free to close it out, or I will if it's still open when the example test case is merged.

@dnkoutso
Copy link
Contributor

@johntmcintosh thanks a lot, will close and merge your examples PR.

dnkoutso added a commit that referenced this issue Oct 20, 2020
* 1-10-stable:
  [CHANGELOG] Add empty Master section
  Release 1.10.0
  Bump Xcodeproj to 1.19
  Update project config to be more compatible for building with Xcode 11.3.1
  Update BananaLib build script to output archives in the gitignored build/DerivedData directory
  Update CoconutLib build script to output archives in the gitignored build/DerivedData directory
  Pull archive and validate script out as its own file
  Build the BananaLib XCFramework, include it in the example project, and update the ArchiveAndValidate script to inspect the results
  Create a BananaLib that will be used to build an xcframework using the Xcode 11 style of debug symbol placement
  Add ArchiveAndValidate aggregate target that ensures the expected dSYMs and BCSymbolMaps are present in the root of the archive
  Build the xcframework and install the pod in the example project
  Create a sample project and build script for generating an xcframework with Xcode 12 style debug symbols
  Add example scheme that was missing
  Update integration-specs submodule pointer
  #10111 - Update XCFramework installation to support Xcode 12 debug symbols placed as siblings of the .framework inside each architecture's directory
dnkoutso added a commit that referenced this issue Oct 20, 2020
* 1-10-stable:
  [CHANGELOG] Add empty Master section
  Release 1.10.0
  Bump Xcodeproj to 1.19
  Update project config to be more compatible for building with Xcode 11.3.1
  Update BananaLib build script to output archives in the gitignored build/DerivedData directory
  Update CoconutLib build script to output archives in the gitignored build/DerivedData directory
  Pull archive and validate script out as its own file
  Build the BananaLib XCFramework, include it in the example project, and update the ArchiveAndValidate script to inspect the results
  Create a BananaLib that will be used to build an xcframework using the Xcode 11 style of debug symbol placement
  Add ArchiveAndValidate aggregate target that ensures the expected dSYMs and BCSymbolMaps are present in the root of the archive
  Build the xcframework and install the pod in the example project
  Create a sample project and build script for generating an xcframework with Xcode 12 style debug symbols
  Add example scheme that was missing
  Update integration-specs submodule pointer
  #10111 - Update XCFramework installation to support Xcode 12 debug symbols placed as siblings of the .framework inside each architecture's directory
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Help from new or existing contributors would be greatly appreciated! r:xcframeworks Related to the support for XCFrameworks
Projects
None yet
Development

No branches or pull requests

3 participants