Skip to content

Commit c95ee5a

Browse files
grabboufacebook-github-bot
authored andcommitted
feat: Enable Hermes to work on iOS (facebook#29914)
Summary: This PR makes it possible to build iOS applications with Hermes. Note that it doesn't work with `use_frameworks!` just yet. Fixes facebook#27845 (by downgrading iOS deployment target for RCT-Folly to 9.0) Fixes facebook#28810 (as above) Checklist: - [x] Adjust release scripts to create Hermes bytecode bundle - [x] 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) - [x] Test on a new React Native application in both Debug and Release (Device) - [x] Test on an RNTester application in both Debug and Release (Device) - [x] Add missing `i386` to Hermes framework and enable Bitcode - [x] Inspect CI failures for possible regressions - [x] Resolve Folly issue as reported facebook#27845 and facebook#28810 - [x] 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] - Introduce `USE_HERMES` variable and test all RNTester configurations on the CI [INTERNAL] - Do not fetch CocoaPods repository since we're using CDN anyway Pull Request resolved: facebook#29914 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") <img width="395" alt="Screenshot 2020-09-09 at 19 22 32" src="https://user-images.githubusercontent.com/2464966/92631584-d7c01d80-f2d1-11ea-9b40-33d73db96a53.png"> Reviewed By: hramos Differential Revision: D24684845 Pulled By: cpojer fbshipit-source-id: 900cbe3bf9398a6fd4a773d552899a001bf5146b
1 parent 0045621 commit c95ee5a

File tree

14 files changed

+121
-70
lines changed

14 files changed

+121
-70
lines changed

.circleci/config.yml

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ defaults: &defaults
2020
- PUBLIC_ANALYSISBOT_GITHUB_TOKEN_A: &github_token_a "78a72af35445ca3f8180"
2121
- PUBLIC_ANALYSISBOT_GITHUB_TOKEN_B: &github_token_b "b1a98e0bbd56ff1ccba1"
2222

23-
2423
# -------------------------
2524
# EXECUTORS
2625
# -------------------------
@@ -152,13 +151,13 @@ commands:
152151
command: cp packages/rn-tester/Podfile.lock packages/rn-tester/Podfile.lock.bak
153152
- restore_cache:
154153
keys:
155-
- v1-pods-{{ .Environment.CIRCLE_JOB }}-{{ checksum "packages/rn-tester/Podfile.lock.bak" }}
156-
- v1-pods-{{ .Environment.CIRCLE_JOB }}-
154+
- v3-pods-{{ .Environment.CIRCLE_JOB }}-{{ checksum "packages/rn-tester/Podfile.lock.bak" }}
155+
- v3-pods-{{ .Environment.CIRCLE_JOB }}-
157156
- steps: << parameters.steps >>
158157
- save_cache:
159158
paths:
160159
- packages/rn-tester/Pods
161-
key: v1-pods-{{ .Environment.CIRCLE_JOB }}-{{ checksum "packages/rn-tester/Podfile.lock.bak" }}
160+
key: v3-pods-{{ .Environment.CIRCLE_JOB }}-{{ checksum "packages/rn-tester/Podfile.lock.bak" }}
162161

163162
download_gradle_dependencies:
164163
steps:
@@ -216,12 +215,12 @@ commands:
216215
jobs:
217216
setup:
218217
parameters:
219-
executor:
220-
type: executor
221-
default: nodelts
222-
checkout_type:
223-
type: string
224-
default: node
218+
executor:
219+
type: executor
220+
default: nodelts
221+
checkout_type:
222+
type: string
223+
default: node
225224
executor: << parameters.executor >>
226225
steps:
227226
- checkout
@@ -261,7 +260,6 @@ jobs:
261260
DANGER_GITHUB_API_TOKEN="$PUBLIC_PULLBOT_GITHUB_TOKEN_A""$PUBLIC_PULLBOT_GITHUB_TOKEN_B" yarn danger ci --use-github-checks
262261
when: always
263262

264-
265263
# -------------------------
266264
# JOBS: Analyze Code
267265
# -------------------------
@@ -351,6 +349,9 @@ jobs:
351349
use_frameworks:
352350
type: boolean
353351
default: false
352+
use_hermes:
353+
type: boolean
354+
default: false
354355
run_unit_tests:
355356
description: Specifies whether unit tests should run.
356357
type: boolean
@@ -407,6 +408,13 @@ jobs:
407408
name: Set USE_FRAMEWORKS=1
408409
command: echo "export USE_FRAMEWORKS=1" >> $BASH_ENV
409410

411+
- when:
412+
condition: << parameters.use_hermes >>
413+
steps:
414+
- run:
415+
name: Set USE_HERMES=1
416+
command: echo "export USE_HERMES=1" >> $BASH_ENV
417+
410418
- run:
411419
name: Setup the CocoaPods environment
412420
command: pod setup
@@ -765,20 +773,31 @@ workflows:
765773
requires:
766774
- setup_android
767775
- test_ios:
768-
name: test_ios_unit
769-
run_disabled_tests: false
776+
name: test_ios_unit_jsc
777+
run_unit_tests: true
778+
requires:
779+
- setup_ios
780+
- test_ios:
781+
name: test_ios_unit_frameworks_jsc
782+
use_frameworks: true
783+
run_unit_tests: true
784+
requires:
785+
- setup_ios
786+
- test_ios:
787+
name: test_ios_unit_hermes
788+
use_hermes: true
770789
run_unit_tests: true
771790
requires:
772791
- setup_ios
773792
- test_ios:
774-
name: test_ios_unit_frameworks
793+
name: test_ios_unit_frameworks_hermes
794+
use_hermes: true
775795
use_frameworks: true
776796
run_unit_tests: true
777797
requires:
778798
- setup_ios
779799
# - test_ios:
780800
# name: test_ios_detox
781-
# run_disabled_tests: false
782801
# run_detox_tests: true
783802
# requires:
784803
# - setup_ios

IntegrationTests/GlobalEvalWithSourceUrlTest.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
'use strict';
1212

13+
import type {ExtendedError} from 'react-native/Libraries/Core/Devtools/parseErrorStack';
14+
1315
const React = require('react');
1416
const ReactNative = require('react-native');
1517
const parseErrorStack = require('react-native/Libraries/Core/Devtools/parseErrorStack');
@@ -31,7 +33,7 @@ class GlobalEvalWithSourceUrlTest extends React.Component<{...}> {
3133
'Expected globalEvalWithSourceUrl(expression) to return a value',
3234
);
3335
}
34-
let syntaxError;
36+
let syntaxError: ?ExtendedError;
3537
try {
3638
global.globalEvalWithSourceUrl('{');
3739
} catch (e) {
@@ -42,7 +44,12 @@ class GlobalEvalWithSourceUrlTest extends React.Component<{...}> {
4244
'Expected globalEvalWithSourceUrl to throw on a syntax error',
4345
);
4446
}
45-
if (!(syntaxError instanceof SyntaxError)) {
47+
// Hermes throws an Error instead of a SyntaxError
48+
// https://github.com/facebook/hermes/issues/400
49+
if (
50+
syntaxError.jsEngine !== 'hermes' &&
51+
!(syntaxError instanceof SyntaxError)
52+
) {
4653
throw new Error(
4754
'Expected globalEvalWithSourceUrl to throw SyntaxError on a syntax error',
4855
);

React-Core.podspec

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,14 @@ Pod::Spec.new do |s|
6363
end
6464

6565
s.subspec "Hermes" do |ss|
66-
ss.platforms = { :osx => "10.14" }
66+
ss.platforms = { :osx => "10.14", :ios => "10.0" }
6767
ss.source_files = "ReactCommon/hermes/executor/*.{cpp,h}",
6868
"ReactCommon/hermes/inspector/*.{cpp,h}",
6969
"ReactCommon/hermes/inspector/chrome/*.{cpp,h}",
7070
"ReactCommon/hermes/inspector/detail/*.{cpp,h}"
7171
ss.pod_target_xcconfig = { "GCC_PREPROCESSOR_DEFINITIONS" => "HERMES_ENABLE_DEBUGGER=1" }
7272
ss.dependency "RCT-Folly/Futures"
73-
ss.dependency "hermes", "~> 0.6.0"
73+
ss.dependency "hermes-engine"
7474
end
7575

7676
s.subspec "DevSupport" do |ss|

React/CxxBridge/RCTCxxBridge.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
#import <jsireact/JSIExecutor.h>
3838
#import <reactperflogger/BridgeNativeModulePerfLogger.h>
3939

40-
#if TARGET_OS_OSX && __has_include(<hermes/hermes.h>)
40+
#if __has_include(<hermes/hermes.h>)
4141
#define RCT_USE_HERMES 1
4242
#endif
4343
#if RCT_USE_HERMES

packages/rn-tester/Podfile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,16 @@ if ENV['USE_FRAMEWORKS'] == '1'
88
use_frameworks!
99
end
1010

11+
if ENV['USE_HERMES'] == '1'
12+
puts "Using Hermes engine"
13+
end
14+
1115
def pods()
1216
project 'RNTesterPods.xcodeproj'
1317

1418
# Enable TurboModule
1519
prefix_path = "../.."
16-
use_react_native!(path:prefix_path)
20+
use_react_native!(path: prefix_path, hermes_enabled: ENV['USE_HERMES'] == '1')
1721
pod 'ReactCommon/turbomodule/samples', :path => "#{prefix_path}/ReactCommon"
1822

1923
# Additional Pods which aren't included in the default Podfile

packages/rn-tester/Podfile.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -505,7 +505,7 @@ SPEC CHECKSUMS:
505505
RCTTypeSafety: 4da4f9f218727257c50fd3bf2683a06cdb4fede3
506506
React: 87b3271d925336a94620915db5845c67c5dbbd77
507507
React-callinvoker: e9524d75cf0b7ae108868f8d34c0b8d7dc08ec03
508-
React-Core: f4eeb7ca3d6a7c2879d1d5b093800f23da9be617
508+
React-Core: 40874579985cf440232e3f33ce172be1f04d4964
509509
React-CoreModules: 87f011fa87190ffe979e443ce578ec93ec6ff4d4
510510
React-cxxreact: de6de17eac6bbaa4f9fad46b66e7f0c4aaaf863d
511511
React-jsi: 790da16b69a61adc36829eed43c44187c1488d10
@@ -528,6 +528,6 @@ SPEC CHECKSUMS:
528528
Yoga: 69ef0b2bba5387523f793957a9f80dbd61e89631
529529
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
530530

531-
PODFILE CHECKSUM: 1d1cdc29892e8a27b768e0e7ce1ead435a53bf0c
531+
PODFILE CHECKSUM: 497dba59a3312b3cb22d518b96672409db90460d
532532

533533
COCOAPODS: 1.10.0

packages/rn-tester/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Before running the app, make sure you ran:
1515
Both macOS and Xcode are required.
1616
- `cd packages/rn-tester`
1717
- Install [Bundler](https://bundler.io/): `gem install bundler`. We use bundler to install the right version of [CocoaPods](https://cocoapods.org/) locally.
18-
- Install Bundler and CocoaPods dependencies: `bundle install && bundle exec pod install`
18+
- Install Bundler and CocoaPods dependencies: `bundle install && bundle exec pod install`. In order to use Hermes engine instead of JSC, run: `USE_HERMES=1 bundle exec pod install` instead.
1919
- Open the generated `RNTesterPods.xcworkspace`. This is not checked in, as it is generated by CocoaPods. Do not open `RNTesterPods.xcodeproj` directly.
2020

2121
### Running on Android

packages/rn-tester/RNTester/AppDelegate.mm

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,15 @@
77

88
#import "AppDelegate.h"
99

10+
#if __has_include(<hermes/hermes.h>)
11+
#define RCT_USE_HERMES 1
12+
#endif
13+
#if RCT_USE_HERMES
14+
#import <React/HermesExecutorFactory.h>
15+
#else
1016
#import <React/JSCExecutorFactory.h>
17+
#endif
18+
1119
#import <React/RCTJSIExecutorRuntimeInstaller.h>
1220
#import <React/RCTBridge.h>
1321
#import <React/RCTBundleURLProvider.h>
@@ -165,7 +173,11 @@ - (void)loadSourceForBridge:(RCTBridge *)bridge
165173
#endif
166174

167175
__weak __typeof(self) weakSelf = self;
176+
#if RCT_USE_HERMES
177+
return std::make_unique<facebook::react::HermesExecutorFactory>(
178+
#else
168179
return std::make_unique<facebook::react::JSCExecutorFactory>(
180+
#endif
169181
facebook::react::RCTJSIExecutorRuntimeInstaller([weakSelf, bridge](facebook::jsi::Runtime &runtime) {
170182
if (!bridge) {
171183
return;

packages/rn-tester/RNTesterIntegrationTests/RCTLoggingTests.m

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ - (void)testLogging
9090

9191
XCTAssertEqual(_lastLogLevel, RCTLogLevelError);
9292
XCTAssertEqual(_lastLogSource, RCTLogSourceJavaScript);
93-
XCTAssertEqualObjects(_lastLogMessage, @"Invariant Violation: Invariant failed");
93+
XCTAssertTrue([_lastLogMessage containsString:@"Invariant Violation: Invariant failed"]);
9494

9595
[_bridge enqueueJSCall:@"LoggingTestModule.logErrorToConsole" args:@[@"Invoking console.error"]];
9696
dispatch_semaphore_wait(_logSem, DISPATCH_TIME_FOREVER);
@@ -114,7 +114,7 @@ - (void)testLogging
114114

115115
XCTAssertEqual(_lastLogLevel, RCTLogLevelError);
116116
XCTAssertEqual(_lastLogSource, RCTLogSourceJavaScript);
117-
XCTAssertEqualObjects(_lastLogMessage, @"Error: Throwing an error");
117+
XCTAssertTrue([_lastLogMessage containsString:@"Error: Throwing an error"]);
118118
}
119119

120120
@end

packages/rn-tester/RNTesterPods.xcodeproj/project.pbxproj

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@
6060
/* End PBXBuildFile section */
6161

6262
/* Begin PBXContainerItemProxy section */
63+
ADA4EF572546F3F8000B7E75 /* PBXContainerItemProxy */ = {
64+
isa = PBXContainerItemProxy;
65+
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
66+
proxyType = 1;
67+
remoteGlobalIDString = 13B07F861A680F5B00A75B9A;
68+
remoteInfo = RNTester;
69+
};
6370
E7DB215822B2F332005AC45F /* PBXContainerItemProxy */ = {
6471
isa = PBXContainerItemProxy;
6572
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
@@ -422,6 +429,7 @@
422429
buildRules = (
423430
);
424431
dependencies = (
432+
ADA4EF582546F3F8000B7E75 /* PBXTargetDependency */,
425433
);
426434
name = RNTesterUnitTests;
427435
productName = RNTesterUnitTests;
@@ -723,6 +731,11 @@
723731
/* End PBXSourcesBuildPhase section */
724732

725733
/* Begin PBXTargetDependency section */
734+
ADA4EF582546F3F8000B7E75 /* PBXTargetDependency */ = {
735+
isa = PBXTargetDependency;
736+
target = 13B07F861A680F5B00A75B9A /* RNTester */;
737+
targetProxy = ADA4EF572546F3F8000B7E75 /* PBXContainerItemProxy */;
738+
};
726739
E7DB215922B2F332005AC45F /* PBXTargetDependency */ = {
727740
isa = PBXTargetDependency;
728741
target = 13B07F861A680F5B00A75B9A /* RNTester */;

scripts/react-native-xcode.sh

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,26 @@ fi
107107
# shellcheck source=/dev/null
108108
source "$REACT_NATIVE_DIR/scripts/node-binary.sh"
109109

110+
[ -z "$HERMES_CLI_PATH" ] && HERMES_CLI_PATH="$PODS_ROOT/hermes-engine/destroot/bin/hermesc"
111+
112+
if [[ -z "$USE_HERMES" && -f "$HERMES_CLI_PATH" ]]; then
113+
echo "Enabling Hermes byte-code compilation. Disable with USE_HERMES=false if needed."
114+
USE_HERMES=true
115+
fi
116+
117+
if [[ $USE_HERMES == true && ! -f "$HERMES_CLI_PATH" ]]; then
118+
echo "error: USE_HERMES is set to true but the hermesc binary could not be " \
119+
"found at ${HERMES_CLI_PATH}. Perhaps you need to run pod install or otherwise " \
120+
"point the HERMES_CLI_PATH variable to your custom location." >&2
121+
exit 2
122+
fi
123+
110124
[ -z "$NODE_ARGS" ] && export NODE_ARGS=""
111125

112126
[ -z "$CLI_PATH" ] && export CLI_PATH="$REACT_NATIVE_DIR/cli.js"
113127

114128
[ -z "$BUNDLE_COMMAND" ] && BUNDLE_COMMAND="bundle"
115129

116-
[ -z "$HERMES_PATH" ] && HERMES_PATH="$PROJECT_ROOT/node_modules/hermes-engine-darwin/destroot/bin/hermesc"
117-
118130
[ -z "$COMPOSE_SOURCEMAP_PATH" ] && COMPOSE_SOURCEMAP_PATH="$REACT_NATIVE_DIR/scripts/compose-source-maps.js"
119131

120132
if [[ -z "$BUNDLE_CONFIG" ]]; then
@@ -136,11 +148,6 @@ case "$PLATFORM_NAME" in
136148
;;
137149
esac
138150

139-
USE_HERMES=
140-
if [[ "$BUNDLE_PLATFORM" == "macos" && -f "$HERMES_PATH" ]]; then
141-
USE_HERMES=true
142-
fi
143-
144151
EMIT_SOURCEMAP=
145152
if [[ ! -z "$SOURCEMAP_FILE" ]]; then
146153
EMIT_SOURCEMAP=true
@@ -180,14 +187,12 @@ else
180187
if [[ $EMIT_SOURCEMAP == true ]]; then
181188
EXTRA_COMPILER_ARGS="$EXTRA_COMPILER_ARGS -output-source-map"
182189
fi
183-
HBC_FILE="$CONFIGURATION_BUILD_DIR/$(basename $BUNDLE_FILE)"
184-
"$HERMES_PATH" -emit-binary $EXTRA_COMPILER_ARGS -out "$HBC_FILE" "$BUNDLE_FILE"
185-
mv "$HBC_FILE" "$DEST/"
186-
BUNDLE_FILE="$DEST/main.jsbundle"
190+
"$HERMES_CLI_PATH" -emit-binary $EXTRA_COMPILER_ARGS -out "$DEST/main.jsbundle" "$BUNDLE_FILE"
187191
if [[ $EMIT_SOURCEMAP == true ]]; then
188-
HBC_SOURCEMAP_FILE="$HBC_FILE.map"
192+
HBC_SOURCEMAP_FILE="$BUNDLE_FILE.map"
189193
"$NODE_BINARY" "$COMPOSE_SOURCEMAP_PATH" "$PACKAGER_SOURCEMAP_FILE" "$HBC_SOURCEMAP_FILE" -o "$SOURCEMAP_FILE"
190194
fi
195+
BUNDLE_FILE="$DEST/main.jsbundle"
191196
fi
192197

193198
if [[ $DEV != true && ! -f "$BUNDLE_FILE" ]]; then

scripts/react_native_pods.rb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
def use_react_native! (options={})
77
# The prefix to the react-native
88
prefix = options[:path] ||= "../node_modules/react-native"
9-
hermes_engine_prefix = options[:hermes_path] ||= "../node_modules/hermes-engine-darwin"
109

1110
# Include Fabric dependencies
1211
fabric_enabled = options[:fabric_enabled] ||= false
@@ -64,7 +63,7 @@ def use_react_native! (options={})
6463

6564
if hermes_enabled
6665
pod 'React-Core/Hermes', :path => "#{prefix}/"
67-
pod 'hermes', :path => hermes_engine_prefix
66+
pod 'hermes-engine'
6867
pod 'libevent', :podspec => "#{prefix}/third-party-podspecs/libevent.podspec"
6968
end
7069
end

0 commit comments

Comments
 (0)