メインコンテンツへスキップ

Bundled Hermes (バンドル版Hermes)

このページでは、Hermes と React Native がどのように構築されているかの概要を説明します。

アプリで Hermes を使用する方法については、別のページで説明しています: Hermes の使用

注意

このページは技術的な詳細であり、Hermes または React Native の上に拡張機能を構築しているユーザーを対象としていることに注意してください。React Native の一般ユーザーは、React Native と Hermes がどのように相互作用するかについて詳細な情報を知る必要はありません。

「バンドルされたHermes」とは

React Native 0.69.0 以降、すべての React Native バージョンは Hermes バージョンと並行してビルドされます。この配布モデルをバンドルされたHermesと呼びます。

0.69 以降、React Native の各バージョンと並行してビルドおよびテストされた JS エンジンを常に使用できます。

「バンドルされたHermes」に移行した理由

これまで、React Native と Hermes は、異なるバージョン管理で 2 つの異なるリリースプロセスに従っていました。異なる番号の異なるリリースがあることで、OSS エコシステムで混乱が生じ、特定の Hermes バージョンが特定の React Native バージョンと互換性があるかどうかが不明確でした (つまり、Hermes 0.11.0 が React Native 0.68.0 などとしか互換性がないことを知る必要がありました)。

Hermes と React Native の両方が JSI コードを共有しています (Hermes はこちらReact Native はこちら)。JSI の 2 つの JSI コピーが同期しなくなると、Hermes のビルドは React Native のビルドと互換性がなくなります。このABI の非互換性の問題について詳しくはこちらをご覧ください。

この問題を克服するために、React Native のリリースプロセスを拡張して Hermes をダウンロードしてビルドし、Hermes のビルド時に JSI のコピーが 1 つだけ使用されるようにしました。

これにより、React Native のバージョンをリリースするたびに Hermes のバージョンをリリースでき、ビルドした Hermes エンジンがリリースする React Native バージョンと完全に互換性があることを確認できます。この Hermes バージョンをリリースしている React Native バージョンと一緒に出荷しているため、バンドルされたHermesという名前が付けられています。

これがアプリ開発者に与える影響

導入部で述べたように、アプリ開発者であれば、この変更は直接的な影響はありません

以下の段落では、私たちが内部で行った変更と、透明性のためにその理由の一部を説明します。

iOSユーザー

iOSでは、使用しているhermes-engineを移動しました。

React Native 0.69より前は、ユーザーはPodをダウンロードしていました (Podspecはこちらで確認できます)。

React Native 0.69では、ユーザーは代わりにreact-native NPMパッケージ内のsdks/hermes-engine/hermes-engine.podspecファイルで定義されているPodspecを使用します。そのPodspecは、React Nativeリリースプロセスの一部としてMavenとReact Native GitHubリリースにアップロードするHermesのプリビルドされたtarballに依存しています (例: このリリースの資産を参照)。

Androidユーザー

Androidでは、デフォルトのテンプレート内のandroid/app/build.gradleファイルを次のように更新します。

差分
dependencies {
// ...

if (enableHermes) {
+ implementation("com.facebook.react:hermes-engine:+") {
+ exclude group:'com.facebook.fbjni'
+ }
- def hermesPath = "../../node_modules/hermes-engine/android/";
- debugImplementation files(hermesPath + "hermes-debug.aar")
- releaseImplementation files(hermesPath + "hermes-release.aar")
} else {
implementation jscFlavor
}
}

React Native 0.69より前は、ユーザーはhermes-engine NPMパッケージからhermes-debug.aarhermes-release.aarを消費していました。

React Native 0.69では、ユーザーはreact-native NPMパッケージ内のandroid/com/facebook/react/hermes-engine/フォルダーにあるAndroidマルチバリアント成果物を消費します。また、React Nativeの将来のバージョンでは、hermes-engineへの依存関係を完全に削除することにも注意してください。

新しいアーキテクチャのAndroidユーザー

ネイティブコードのビルド設定の性質(つまり、NDKの使用方法)により、新しいアーキテクチャのユーザーはHermesをソースからビルドします

これにより、新しいアーキテクチャのユーザー向けのReact NativeとHermesのビルドメカニズムが統一されます(両方のフレームワークをソースからビルドします)。これは、そのようなAndroidユーザーが最初のビルド時にビルド時間のパフォーマンスヒットを経験する可能性があることを意味します。

ビルド時間を最適化し、ビルドへの影響を減らすための手順は、このページにあります:ビルドフェーズの高速化

Windowsで新しいアーキテクチャをビルドするAndroidユーザー

Windowsマシンで新しいアーキテクチャを使用してReact Nativeアプリをビルドするユーザーは、ビルドが正しく機能するように次の追加手順に従う必要があります

ユーザーは別のエンジンをまだ使用できますか?

はい、ユーザーはHermesを有効/無効にすることができます(AndroidではenableHermes変数、iOSではhermes_enabled)。「バンドルされたHermes」の変更は、Hermesがどのようにビルドされ、バンドルされるかにのみ影響します。

React Native 0.70以降、enableHermes/hermes_enabledのデフォルトはtrueです。

これがコントリビューターと拡張機能開発者に与える影響

React Nativeのコントリビューターである場合、またはReact NativeまたはHermesの上に拡張機能を構築している場合は、バンドルされたHermesがどのように機能するかを説明しますので、引き続きお読みください。

バンドルされたHermesはどのように機能しているのか?

このメカニズムは、facebook/hermesリポジトリからHermesソースコードを含むtarballをダウンロードしてfacebook/react-nativeリポジトリに配置することに依存しています。他のネイティブ依存関係 (Folly、Glogなど) にも同様のメカニズムがあり、Hermesも同じ設定に従うように調整しました。

mainブランチからReact Nativeをビルドする場合、facebook/hermesのmainブランチのtarballをフェッチし、React Nativeのビルドプロセスの一部としてビルドします。

リリースブランチ (例えば0.69-stable) からReact Nativeをビルドする場合、代わりにHermesリポジトリのタグを使用して、2つのリポジトリ間のコードを同期させます。使用される特定のタグ名は、その後、リリースブランチのReact Native内のsdks/.hermesversionファイルに保存されます (例: これは0.69リリースブランチのファイルです)。

ある意味、このアプローチはgit submoduleに似ていると考えることができます。

Hermesの上に構築している場合、React Nativeのバージョンがタグ名に指定されているため (例: hermes-2022-05-20-RNv0.69.0-ee8941b8874132b8f83e4486b63ed5c19fc3f111)、これらのタグに頼ってReact Nativeのビルド時に使用されたHermesのバージョンを理解できます。

Androidの実装詳細

Androidでこれを実装するために、React Nativeの/ReactAndroid/hermes-engine内に新しいビルドを追加しました。これは、Hermesのビルドと消費のためのパッケージングを行います (詳細はこちら)。

これで、次を呼び出してHermesエンジンのビルドをトリガーできます。

bash
// Build a debug version of Hermes
./gradlew :ReactAndroid:hermes-engine:assembleDebug
// Build a release version of Hermes
./gradlew :ReactAndroid:hermes-engine:assembleRelease

React Nativeのmainブランチから。

ビルドはNDKバージョンのツールを使用するように設定されているため、マシンに余分なツール (cmakeninjapython3など) をインストールする必要はありません。

Gradleコンシューマー側では、コンシューマー側に小さな改善も施しました。releaseImplementationdebugImplementationからimplementationに移行しました。これは、新しいhermes-engine Androidアーティファクトがバリアント対応であり、エンジンのデバッグビルドをアプリのデバッグビルドと適切に照合できるためです。ここではカスタム設定は必要ありません (stagingやその他のビルドタイプ/フレーバーを使用している場合でも)。

しかし、これにより、テンプレートにこの行が必要になりました

exclude group:'com.facebook.fbjni'

これは、React Nativeが非プリファブアプローチ(つまり、.aarを解凍して.soファイルを抽出する)を使用してfbjniを消費しているために必要です。Hermes-engineやその他のライブラリは、代わりにプリファブを使用してfbjniを消費しています。将来、この問題に対処することを検討しており、Hermesのインポートが1行になるようにします。

iOS実装の詳細

iOSの実装は、次の場所に存在する一連のスクリプトに依存しています。

  • /scripts/hermes。これらのスクリプトには、Hermes tarballをダウンロードし、解凍し、iOSビルドを構成するロジックが含まれています。hermes_enabledフィールドがtrueに設定されている場合、pod install時に呼び出されます。
  • /sdks/hermes-engine。これらのスクリプトには、実際にHermesをビルドするビルドロジックが含まれています。これらはfacebook/hermesリポジトリからコピーされ、React Native内で適切に動作するように調整されました。特に、utilsフォルダー内のスクリプトは、すべてのMacプラットフォーム用のHermesのビルドを担当しています。

Hermesは、CircleCIのbuild_hermes_macosジョブの一部としてビルドされます。このジョブは成果物としてtarballを生成し、公開されたReact Nativeリリースを使用するとhermes-engine podspecによってダウンロードされます (build_hermes_macosでReact Native 0.69用に作成された成果物の例はこちら)。

プリビルドされたHermes

使用されているReact Nativeバージョンにプリビルドされた成果物がない場合(つまり、mainブランチからReact Nativeを操作している場合)、Hermesはソースからビルドする必要があります。まず、pod install中にHermesコンパイラのhermescがmacOS用にビルドされ、次にbuild-hermes-xcode.shスクリプトを使用してXcodeビルドパイプラインの一部としてHermes自体がビルドされます。

ソースからHermesをビルドする

mainブランチからReact Nativeを使用する場合、Hermesは常にソースからビルドされます。安定版のReact Nativeを使用している場合、CocoaPodsを使用するときにCI環境変数をtrueに設定することで、Hermesをソースからビルドするように強制できます: CI=true pod install

デバッグシンボル

Hermesのプリビルドされた成果物には、デフォルトでデバッグシンボル(dSYMs)が含まれていません。将来的には、これらのデバッグシンボルを各リリースで配布する予定です。それまでは、Hermesのデバッグシンボルが必要な場合は、Hermesをソースからビルドする必要があります。hermes.framework.dSYMは、Hermesの各フレームワークと一緒にビルドディレクトリに作成されます。

この変更が私に影響を与えているようです

これは本質的に、Hermesがどこでビルドされ、2つのリポジトリ間でコードがどのように同期されるかという組織的な変更であることを強調したいと思います。この変更はユーザーにとって完全に透過的であるべきです。

これまで、React Nativeの特定のバージョン向けにHermesのリリースを行っていました (例: RN0.68.x向けのv0.11.0)。

「バンドルされたHermes」では、代わりに、React Nativeの特定のバージョンがリリースされたときに使用されたバージョンを表すタグに依存できます。