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

feat: Enable Hermes to work on iOS #29914

Open
wants to merge 42 commits into
base: master
from

Conversation

@grabbou
Copy link
Collaborator

@grabbou grabbou commented Sep 9, 2020

Summary

This PR makes it possible to build iOS applications with Hermes.

Fixes #27845 (by downgrading iOS deployment target for RCT-Folly to 9.0)
Fixes #28810 (as above)

Checklist:

  • Adjust release scripts to create Hermes bytecode bundle
  • Release new Hermes npm package that includes iOS files (unreleased right now, if you want to try locally, you have to clone Hermes and yarn link its master to this project)
  • Test on a new React Native application in both Debug and Release (Device)
  • Test on an RNTester application in both Debug and Release (Device)
  • Add missing i386 to Hermes framework and enable Bitcode
  • Inspect CI failures for possible regressions
  • Resolve Folly issue as reported #27845 and #28810
  • Release new Hermes and test against it that everything works

Changelog

[IOS] [FEATURE] - Enable Hermes on iOS
[INTERNAL] - Upgrade to CocoaPods 1.10.0 to resolve Xcode 12.0 issues
[INTERNAL] - Upgrade to Xcode 12.0 on the CircleCI
[INTERNAL] - Fix building RNTester in Release mode
[INTERNAL] - Fix build-time errors of libevent with use_frameworks!
[INTERNAL] - Do not fetch CocoaPods repository since we're using CDN anyway

Test Plan

Turn on hermes_enabled to true in your Podfile, install pods, and run the iOS application. Your app should be running Hermes now.

Preview: (note "Engine: Hermes")

Screenshot 2020-09-09 at 19 22 32

@grabbou grabbou requested review from cpojer, hramos and mhorowitz as code owners Sep 9, 2020
React-Core.podspec Outdated Show resolved Hide resolved
package.json Outdated Show resolved Hide resolved
package.json Outdated Show resolved Hide resolved
Copy link
Collaborator

@alloy alloy left a comment

This is amazing! Great stuff as always, @grabbou 👏

Thus far the changes are looking good to me, but will review again when it's no longer marked as WIP.

@analysis-bot
Copy link

@analysis-bot analysis-bot commented Sep 9, 2020

Platform Engine Arch Size (bytes) Diff
android hermes arm64-v8a 7,211,837 -182,156
android hermes armeabi-v7a 6,861,013 -156,200
android hermes x86 7,646,477 -185,632
android hermes x86_64 7,537,421 -188,270
android jsc arm64-v8a 9,370,806 -165,392
android jsc armeabi-v7a 9,012,102 -139,124
android jsc x86 9,233,530 -167,344
android jsc x86_64 9,810,678 -171,906

Base commit: 36b0f7d

@grabbou

This comment was marked as off-topic.

@TheSavior

This comment has been hidden.

@TheSavior TheSavior mentioned this pull request Sep 12, 2020
0 of 3 tasks complete
@mrousavy

This comment was marked as off-topic.

@sunnylqm

This comment was marked as off-topic.

scripts/hermes-binary.sh Outdated Show resolved Hide resolved
@grabbou

This comment has been hidden.

friederbluemle and others added 3 commits Sep 4, 2020
Summary:
The nested `.gitattributes` file in `packages/react-native-codegen/android/` caused some confusion on Linux and macOS, causing Git to show `packages/react-native-codegen/android/gradlew.bat` as modified (CRLF removed, LF added).

Instead of relying on repo-local `.gitattributes` files to convert endings in the working directory, the files should be committed to source control with the correct line endings in the first place. There is no reason to convert LF endings in .sh and many other file to CRLF on Windows (maybe this was an issue a long time ago, but unless Notepad is used this won't be a problem for practically all modern editors).

Also fixed the line endings of `scripts/launchPackager.bat` which was incorrectly committed as LF.

## Changelog

[Internal] [Fixed] - Line endings and .gitattributes

Pull Request resolved: #29792

Test Plan: Clone repo on Linux, macOS, and Windows, and make sure no modified files show up.

Reviewed By: fkgozali

Differential Revision: D23546135

Pulled By: mdvacca

fbshipit-source-id: 1572fcb959212f212b137066f1aa66f0bb6e86c3
@grabbou

This comment has been hidden.

@grabbou
Copy link
Collaborator Author

@grabbou grabbou commented Oct 26, 2020

Note: Upgrading to CocoaPods 1.10.0 in this PR to resolve #29984

@alloy
Copy link
Collaborator

@alloy alloy commented Oct 27, 2020

@grabbou Looking at the CI output, it doesn't look like the pod install step is using the Gemfile that locks the CP version, it should be bundle exec pod install. So perhaps it’s still using an older version of CP?

@hramos
Copy link
Contributor

@hramos hramos commented Oct 27, 2020

@grabbou Looking at the CI output, it doesn't look like the pod install step is using the Gemfile that locks the CP version, it should be bundle exec pod install. So perhaps it’s still using an older version of CP?

Correct, we should be using bundle exec there. I have an internal diff (external PR copy here: #30250) which makes this change, but it's currently blocked on Xcode 12.1.0 becoming available on our internal CI. Feel free to make the bundle exec change as part of this PR if necessary.

@grabbou
Copy link
Collaborator Author

@grabbou grabbou commented Oct 29, 2020

@hramos I'll do that - thank you @alloy for spotting it.

@grabbou
Copy link
Collaborator Author

@grabbou grabbou commented Oct 29, 2020

FYI @hramos, I bumped Xcode to 12 and it fixed the build time errors I was getting on the CI. I will rebase once your PR is merged and remove my commits.

@@ -90,7 +90,7 @@ - (void)testLogging

XCTAssertEqual(_lastLogLevel, RCTLogLevelError);
XCTAssertEqual(_lastLogSource, RCTLogSourceJavaScript);
XCTAssertEqualObjects(_lastLogMessage, @"Invariant Violation: Invariant failed");
XCTAssertTrue([_lastLogMessage containsString:@"Invariant Violation: Invariant failed"]);

This comment has been minimized.

@grabbou

grabbou Oct 29, 2020
Author Collaborator

js engine: Hermes is added to the end of the error message / invariant when Hermes is on. Replaced with containsString to make it more universal.

CC: @alloy

@@ -114,7 +114,7 @@ - (void)testLogging

XCTAssertEqual(_lastLogLevel, RCTLogLevelError);
XCTAssertEqual(_lastLogSource, RCTLogSourceJavaScript);
XCTAssertEqualObjects(_lastLogMessage, @"Error: Throwing an error");
XCTAssertTrue([_lastLogMessage containsString:@"Error: Throwing an error"]);

This comment has been minimized.

@grabbou

grabbou Oct 29, 2020
Author Collaborator

On that note, shall we have two targets for integration tests and two targets for unit tests and run them on both Hermes and JSC? To be honest, it sounds to me like an overkill, but would like to discuss this idea.

Right now, Hermes is turned on by default in RNTester on iOS. As a result, all tests that we run are against Hermes.

This comment has been minimized.

@alloy

alloy Oct 30, 2020
Collaborator

I wonder if we can achieve that simply by passing something like GCC_PREPROCESSOR_DEFINITIONS ="RCT_USE_HERMES=0" to the xcodebuild command, rather than adding extra targets.

Either way, it might be better to do that in a follow-up PR?

@grabbou
Copy link
Collaborator Author

@grabbou grabbou commented Oct 29, 2020

The good news for anyone who is following this PR is - the build failures on CircleCI are finally gone! This week I spent on trying to understand why everything works and is green on my laptop and breaks on the CI.

My plan for tomorrow is to make both test_ios_unit and test_ios_unit_frameworks green and #shipit.

@@ -42,7 +42,8 @@ class GlobalEvalWithSourceUrlTest extends React.Component<{...}> {
'Expected globalEvalWithSourceUrl to throw on a syntax error',
);
}
if (!(syntaxError instanceof SyntaxError)) {
// Hermes throws an Error, not a SyntaxError
if (syntaxError.jsEngine !== 'hermes' && !(syntaxError instanceof SyntaxError)) {

This comment has been minimized.

@grabbou

grabbou Oct 30, 2020
Author Collaborator

The exact error is:

Error: Exception in HostFunction: Compiling JS failed: 1:2:'}' expected at end of block

Is this a bug or a known characteristics of Hermes?

CC: @Huxpro

This comment has been minimized.

@Huxpro

Huxpro Oct 30, 2020
Contributor

@grabbou It's kinda related to a known problem that is complex and not going to be resolved anytime soon. It's complex since it's not so much of Hermes but the way we are building and linking our apps. I think we can leave the code this way for now and file an issue against the Hermes repo so we can track it down later.

@analysis-bot
Copy link

@analysis-bot analysis-bot commented Oct 30, 2020

Platform Engine Arch Size (bytes) Diff
ios - universal n/a --

Base commit: 36b0f7d

@analysis-bot
Copy link

@analysis-bot analysis-bot commented Oct 30, 2020

Platform Engine Arch Size (bytes) Diff
ios - universal n/a --

Base commit: 36b0f7d

@hramos
Copy link
Contributor

@hramos hramos commented Oct 30, 2020

@grabbou no need to wait for my PR to land first, don't let that block you, I can rebase after your changes land. As for the failing Android tests, we landed some fixes to master recently.

@alloy
Copy link
Collaborator

@alloy alloy commented Oct 30, 2020

Current status is:

  1. This branch builds when building static libraries. When building dynamic frameworks it fails building the libevent framework, for this issue I have a PR up here: callstack#26
  2. After fixing that, I stumbled on the issue of RN master’s copy of JSI being out of sync with JSI that comes with hermes v0.7.1. Specifically this recent RN commit.
  3. To at least try to get this PR into a mergeable state, I wanted to then switch to consumer Hermes from master, but ran into the issue of Hermes master failing to build for iOS. I’ve tracked the issue down to this Hermes commit which makes the hermesc binary a dependency, which we don’t build for iOS.

Reverting that commit does fix the iOS build again, but I’m unsure of the ramifications. The proper fix would be to first build the macOS artefacts and then re-use its hermesc artefact during the iOS build, but that might make this PR drag on again.


@Huxpro @TheSavior Do you have any thoughts on the best recourse/prioritization?

Possible options I see are the following:

Fastest turnaround time

This could technically be released in a stable RN release.

  1. Revert the commit mentioned above in #2 in RN master.
  2. Revert the commit mentioned above in #3 in hermes master.
  3. Finish this PR by using the existing Hermes v0.7.1 release.

Medium turnaround time

This can not be released in a stable RN release.

  1. Revert the commit mentioned above in #3 in hermes master.
  2. Finish this PR by temporarily pointing the hermes dependency to hermes master.
  3. Potentially make the proper fix to first build macOS artefacts and use the resulting hermesc binary.
  4. [Soon-ish] release Hermes v0.7.2 with a JSI version matching RN master’s.
  5. Update RN’s hermes dependency to the prebuilt version of v0.7.2.

Longest turnaround time

This can definitely be released in a stable RN release.

  1. Revert the commit mentioned above in #3 in hermes master or make the proper fix to first build macOS artefacts and use the resulting hermesc binary.
  2. Release Hermes v0.7.2 with a JSI version matching RN master’s.
  3. Finish this PR by pointing RN’s hermes dependency to the prebuilt version of v0.7.2.
@Huxpro
Copy link
Contributor

@Huxpro Huxpro commented Oct 30, 2020

@alloy thanks for the detailed summary! It seems like the real road blocking issue was just the JSI getting out of sync by d8b0e9d and we only need to do 2 and 3 of the "longest turnaround time" option to get this PR rolling again.

We are working on a patch that include facebook/hermes@73594b5 and its dependencies onto the Hermes release-v0.7 branch to hopefully have a v0.7.2 that is JSI compatible with RN master's. By this way we don't need to revert from either RN or Hermes master. I'll keep everyone posted once it's merged.

After fixing that, I stumbled on the issue of RN master’s copy of JSI being out of sync with JSI that comes with hermes v0.7.1.

It would be helpful to know what are the steps to repro so we can verify if the patch resolve the issue. I can still build RN Tester app on Android on RN master with the hermes-engine-v0.7.1 npm package. Starting heap profiler does not crash the app but disabling it does. I'm not sure if that is the crash you encountered or another one.

To at least try to get this PR into a mergeable state, I wanted to then switch to consumer Hermes from master, but ran into the issue of Hermes master failing to build for iOS. I’ve tracked the issue down to this Hermes commit which makes the hermesc binary a dependency, which we don’t build for iOS.

The proper fix would be to first build the macOS artefacts and then re-use its hermesc artefact during the iOS build, but that might make this PR drag on again.

Yeah, that would be the proper fix. At some point we'll need a hermes v0.8.0 with the fix for RN 0.65, but that could be months. For now, we should probably not consider using Hermes from master for RN 0.64 a very feasible option.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.