本文へスキップ

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

HermesとReact NativeはどちらもJSIコードを共有しています(HermesはこちらReact Nativeはこちら)。JSIの2つのコピーが同期しなくなると、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-nativeNPMパッケージの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-engineNPMパッケージからhermes-debug.aarhermes-release.aarを使用していました。

React Native 0.69では、ユーザーはreact-nativeNPMパッケージの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/react-nativeリポジトリ内でfacebook/hermesリポジトリからHermesソースコードを含むtarballをダウンロードすることに依存しています。他のネイティブ依存関係(Folly、Glogなど)についても同様のメカニズムが導入されており、Hermesも同様の設定に従うように調整しました。

mainからReact Nativeをビルドする場合、facebook/hermesmainのtarballを取得し、React Nativeのビルドプロセスの一部としてビルドします。

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

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

Hermesを基盤として開発する場合、タグを使用してReact Nativeのビルドに使用されたHermesのバージョンを確認できます。React Nativeのバージョンはタグ名に指定されています(例: `hermes-2022-05-20-RNv0.69.0-ee8941b8874132b8f83e4486b63ed5c19fc3f111`)。

Android実装の詳細

Androidでの実装では、React Nativeの` /ReactAndroid/hermes-engine`内にHermesのビルドとパッケージ化を処理する新しいビルドを追加しました(詳細はここを参照)。

Hermesエンジンのビルドは、

// 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バージョンのツールを使用するように構成されているため、マシンに`cmake`、`ninja`、`python3`などの追加ツールをインストールする必要はありません。

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

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

exclude group:'com.facebook.fbjni'

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

iOS実装の詳細

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

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

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

プリビルドHermes

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

ソースからのHermesビルド

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

デバッグシンボル

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

この変更が影響していると思います

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

従来、特定のバージョンのReact Nativeに対してHermesのリリースを作成していました(例:`RN0.68.x 用の v0.11.0`)。

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