-
Notifications
You must be signed in to change notification settings - Fork 58
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
Flatten embedded frameworks when generating XCFramework #5968
Flatten embedded frameworks when generating XCFramework #5968
Conversation
Add `flattenFrameworks` helper in xcframework build script
1b71a87
to
64a5fed
Compare
Wanna run full suite of Android and iOS UI tests? Click here and 'Approve' CI job! |
# | ||
# Notice how we loop on $PLATFORM_IOS as a way to get the frameworks for both platforms. | ||
# We could use either platform to achieve the same result. | ||
for FRAMEWORK in $(find "$ARCHIVES_ROOT/$PLATFORM_IOS.xcarchive/Products/Library/Frameworks" -type d -name "*.framework"); | ||
for FRAMEWORK in "$ARCHIVES_ROOT/$PLATFORM_IOS.xcarchive/Products/Library/Frameworks"/*; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change implies that we'll only loop over the frameworks at the root level. Embedded frameworks (i.e. nested in framework folders) will be skipped as it should have been processed in the previous step.
function flattenFrameworks { | ||
PLATFORM=$1 | ||
|
||
for FRAMEWORK in "$ARCHIVES_ROOT/$PLATFORM.xcarchive/Products/Library/Frameworks"/*; | ||
do | ||
CURRENT_FRAMEWORK_NAME=$(basename "$FRAMEWORK" .framework) | ||
|
||
if [[ -d "$FRAMEWORK/Frameworks" ]]; then | ||
log "[$PLATFORM] $CURRENT_FRAMEWORK_NAME has embedded frameworks, flattening them..." | ||
|
||
for EMBEDDED_FRAMEWORK in "$FRAMEWORK/Frameworks"/*; | ||
do | ||
CURRENT_EMBEDDED_FRAMEWORK_NAME=$(basename "$EMBEDDED_FRAMEWORK" .framework) | ||
|
||
log 'package' "Flattening $CURRENT_EMBEDDED_FRAMEWORK_NAME" | ||
|
||
mv "$EMBEDDED_FRAMEWORK" "$ARCHIVES_ROOT/$PLATFORM.xcarchive/Products/Library/Frameworks" | ||
done | ||
rm -rf "$FRAMEWORK/Frameworks" | ||
fi | ||
done |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function simply moves embedded frameworks (i.e. nested under the Frameworks
folder within another framework) to the root level.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious if this would be enough to make it work, i.e. I wonder if we don't need to update some @loadpath
(via some install_name_tool
dance) so that the dynamic loader (dyld) would be able to properly load the (ex-embded) framework from the parent framework (i.e. load Hermes from Gutenberg) correctly (especially if the load path as printed by otool -L
would show Hermes being referenced as a relative path from Gutenberg) 🤔
I think that if you run otool -L DerivedData/Build/Products/…/WordPress.app/WordPress
on the compiled and linked WordPress executable after it has integrated the Gutenberg.xcframework
via CocoaPods, you should be able to check that both Gutenberg and Hermes are referenced as @rpath/[Name].framework/[Name]
? Though I'd be curious to see if otool -L DerivedData/Build/Products/…/WordPress.app/Frameworks/Gutenberg.framework/Gutenberg
would reference Hermes itself (and if so, how) or not at that point (I'm not familiar with the setup that Gio put in place and how Gutenberg and Hermes interact / might have link-time dependencies on each other)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In practice, if we can find out how to do it, I think it would be better to have the archive action generate Hermes and Gutenberg as separate flattened frameworks to begin with at compile time, rather than have Hermes be generated as an embed framework by Xcode in the xcarchive
it generates, and then have our scripts try to de-embed it manually after the fact… 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you so much @AliSoftware for the feedback about this approach 🙇 !
I'm curious if this would be enough to make it work, i.e. I wonder if we don't need to update some
@loadpath
(via someinstall_name_tool
dance) so that the dynamic loader (dyld) would be able to properly load the (ex-embded) framework from the parent framework (i.e. load Hermes from Gutenberg) correctly (especially if the load path as printed byotool -L
would show Hermes being referenced as a relative path from Gutenberg) 🤔
I'm testing the changes in combination with wordpress-mobile/WordPress-iOS#21021, and so far building the app locally for both the simulator and physical device seems to work. I can take a quick look at the commands you share to validate that references are not broken, although I understand I'd probably experience a build failure or runtime crash in that case 🤔.
In practice, if we can find out how to do it, I think it would be better to have the archive action generate Hermes and Gutenberg as separate flattened frameworks to begin with at compile time, rather than have Hermes be generated as an embed framework by Xcode in the
xcarchive
it generates, and then have our scripts try to de-embed it manually after the fact… 🤔
Good idea. I assume this would be done by adding a phase script in the Xcode project to flatten the embedded frameworks, is this accurate?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume this would be done by adding a phase script in the Xcode project to flatten the embedded frameworks, is this accurate?
I was more thinking about selecting "Do Not Embed" in the "Frameworks and Libraries" settings of the XCFrameworkScaffold.xcodeproj
:
Though to be honest, I haven't looked at how the Gutenberg.framework compilation works (how the scaffold project is configured in details and how this interacts with the build scripts like build.sh
and the declarations in the podspec
and the potential custom scripts / post_install
hooks in client app and all that), so I'd prefer to let @mokagio chime in on the details there than trying to grasp at straws without knowing the details about the bigger picture.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that if you run
otool -L DerivedData/Build/Products/…/WordPress.app/WordPress
on the compiled and linked WordPress executable after it has integrated theGutenberg.xcframework
via CocoaPods, you should be able to check that both Gutenberg and Hermes are referenced as@rpath/[Name].framework/[Name]
? Though I'd be curious to see ifotool -L DerivedData/Build/Products/…/WordPress.app/Frameworks/Gutenberg.framework/Gutenberg
would reference Hermes itself (and if so, how) or not at that point (I'm not familiar with the setup that Gio put in place and how Gutenberg and Hermes interact / might have link-time dependencies on each other)
I've run the commands and seems the reference is correct. For both the app's executable and Gutenberg framwork, they point to Hermes (@rpath/hermes.framework
).
Result of otool -L DerivedData/.../Build/Products/Debug-iphoneos/Jetpack.app/Jetpack
:
[...]
@rpath/Gutenberg.framework/Gutenberg (compatibility version 1.0.0, current version 1.0.0)
@rpath/hermes.framework/hermes (compatibility version 0.12.0, current version 0.12.0)
[...]
Result of otool -L DerivedData/.../Build/Products/Debug-iphoneos/Jetpack.app/Frameworks/Gutenberg.framework/Gutenberg
:
[...]
@rpath/Gutenberg.framework/Gutenberg (compatibility version 1.0.0, current version 1.0.0)
@rpath/hermes.framework/hermes (compatibility version 0.12.0, current version 0.12.0)
[...]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was more thinking about selecting "Do Not Embed" in the "Frameworks and Libraries" settings of the XCFrameworkScaffold.xcodeproj:
FWIW, #5973 uses that approach.
echo "--- :package: Run bundle prep work" | ||
npm run prebundle:js | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I skipped this step to speed up the bundle generation, it should be reverted before merging the PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm surprised the build worked without it 🤔 Maybe it's no longer necessary and we should not add it back?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When installing the dependencies, we download all localizations for all plugins via the command i18n:check-cache
. This includes tons of strings that are not even used in the editor, leading to a bigger bundle with an increase of around an extra 20 MB. The command i18n:update
analyzes the strings referenced within the bundles generated by React Native, and removes the unneeded ones. As well as it generates the localization strings files (example) to incorporate into the host apps.
Line 61 in 6f1d7e7
"postinstall": "patch-package && npm run clean:gutenberg:distclean && npm ci --prefix gutenberg && npm run i18n:check-cache && ./bin/run-jetpack-command.sh \"install --ignore-scripts\"", |
Hence, the prebundle
command is not strictly needed when generating the bundle, but strongly recommended to optimize the bundle size. In this regard, now that we'll generate both Android and iOS bundles in CI, I'm thinking of reconsidering this step for the sake of reducing the build time. In the end, we'd only need the optimal bundle when releasing a new version of Gutenberg Mobile, so we might add a condition to the job to run prebundle
in that situation.
Line 73 in a842544
"prebundle:js": "npm run i18n:update", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the explanation @fluiddot.
In the end, we'd only need the optimal bundle when releasing a new version of Gutenberg Mobile, so we might add a condition to the job to run
prebundle
in that situation.
I see... We could do this by checking if the CI build is running on a tag
This reverts commit 603dd7e.
Closing this PR in favor of #5973. |
The goal of this PR is to flatten frameworks nested under the
Frameworks
folder of other frameworks. Especially, the Hermes framework which is nested under the Gutenberg framework.This has been achieved by simply moving the embedded frameworks to the root level (e03356d), plus including the Hermes framework in the vendor framework list of the Gutenberg
podspec
(355f431).To test
Preparation:
gutenberg/tweak-pod-deps-by-rn-version
from the WP-iOS repository (PR reference).Test via CI
Build iOS RN XCFramework & Publish to S3
CI job finishes.64a5fed3fd28e7fa4aa25edc78f39b76617e5f71
.bundle install && bundle exec pod install
.NOTE: If any error is encountered, try to run the command
rm -rf build && rm -rf Pods
before installing Pods.Test via local installation
podspec
reference to:../gutenberg-mobile/ios-xcframework/Gutenberg.podspec
bundle install && bundle exec pod install
.NOTE: If any error is encountered, try to run the command
rm -rf build && rm -rf Pods
before installing Pods.PR submission checklist: