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

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つのコピーが同期されなくなると、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ファイルを次のように更新します。

diff
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/react-nativeリポジトリ内のfacebook/hermesリポジトリからHermesソースコードを含むtarballをダウンロードすることに依存しています。他のネイティブ依存関係(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サブモジュールに似ていると考えることができます。

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

Androidの実装詳細

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

これで、次を呼び出すことで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ジョブの一部として構築されます。このジョブは、公開されたReact Nativeリリースを使用する際にhermes-engine podspecによってダウンロードされるtarballを成果物として生成します(これはbuild_hermes_macosでReact Native 0.69用に作成された成果物の例です)。

プレビルドされたHermes

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

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のリリースを行っていました(例: v0.11.0 for RN0.68.x)。

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