打包 Hermes
本页概述了 Hermes 和 React Native 的构建方式。
¥This page gives an overview of how Hermes and React Native are built.
如果你正在寻找有关如何在应用中使用 Hermes 的说明,你可以在其他页面上找到说明:使用 Hermes
¥If you're looking into instructions on how to use Hermes in your app, you can find instructions on this other page: using Hermes
请注意,此页面作为技术深入探讨,针对在 Hermes 或 React Native 之上构建扩展的用户。React Native 的一般用户不需要了解有关 React Native 和 Hermes 如何交互的深入信息。
¥Please note that this page serves as a technical deep dive and is targeted for users which are building extensions on top of Hermes or React Native. General users of React Native should not need to know in-depth information on how React Native and Hermes interact.
什么是 '打包 Hermes'
¥What is 'Bundled Hermes'
从 React Native 0.69.0 开始,React Native 的每个版本都将与 Hermes 版本一起构建。我们将这种分发模式称为 Bundled Hermes。
¥Starting with React Native 0.69.0, every version of React Native will be built alongside to a Hermes version. We call this distribution model Bundled Hermes.
从 0.69 开始,你将始终拥有一个与你可以使用的每个 React Native 版本一起构建和测试的 JS 引擎。
¥From 0.69 on, you will always have a JS engine that has been built and tested alongside each React Native version that you can use.
为什么我们迁移到 '打包 Hermes'
¥Why we moved to 'Bundled Hermes'
从历史上看,React Native 和 Hermes 遵循两种不同的发布流程和不同的版本控制。具有不同数字的不同版本会在 OSS 生态系统中造成混乱,不清楚特定版本的 Hermes 是否与特定版本的 React Native 兼容(即,你需要知道 Hermes 0.11.0 仅与 React Native 兼容) 0.68.0 等)
¥Historically, React Native and Hermes followed two distinct release processes with distinct versioning. Having distinct releases with distinct numbers created confusion in the OSS ecosystem, where it was not clear if a specific version of Hermes was compatible with a specific version of React Native (i.e. you needed to know that Hermes 0.11.0 was compatible only with React Native 0.68.0, etc.)
Hermes 和 React Native 共享 JSI 代码(Hermes 在这里 和 React Native 在此)。如果 JSI 的两个 JSI 副本不同步,Hermes 的构建将与 React Native 的构建不兼容。你可以阅读有关此 ABI 不兼容问题在这里 的更多信息。
¥Both Hermes and React Native, share the JSI code (Hermes here and React Native here). If the two JSI copies of JSI get out of sync, a build of Hermes won't be compatible with a build of React Native. You can read more about this ABI incompatibility problem here.
为了克服这个问题,我们扩展了 React Native 发布流程来下载和构建 Hermes,并确保构建 Hermes 时仅使用一份 JSI 副本。
¥To overcome this problem, we've extended the React Native release process to download and build Hermes and made sure only one copy of JSI is used when building Hermes.
得益于此,每当我们发布 React Native 版本时,我们都可以发布一个版本的 Hermes,并确保我们构建的 Hermes 引擎与我们正在发布的 React Native 版本完全兼容。我们正在做,因此得名打包 Hermes。
¥Thanks to this, we can release a version of Hermes whenever we release a version of React Native, and be sure that the Hermes engine we built is fully compatible with the React Native version we're releasing. We're shipping this version of Hermes alongside the React Native version we're doing, hence the name Bundled Hermes.
这将如何影响应用开发者
¥How this will impact app developers
正如简介中提到的,如果你是应用开发者,此更改不应直接影响你。
¥As mentioned in the introduction, if you're an app developer, this change should not affect you directly.
为了透明起见,以下段落描述了我们在幕后所做的更改并解释了一些基本原理。
¥The following paragraphs describe which changes we did under the hood and explains some of the rationales, for the sake of transparency.
iOS 用户
¥iOS Users
在 iOS 上,我们已移动你正在使用的 hermes-engine
。
¥On iOS, we've moved the hermes-engine
you're using.
在 React Native 0.69 之前,用户会下载一个 pod(在这里你可以找到 podspec)。
¥Prior to React Native 0.69, users would download a pod (here you can find the podspec).
在 React Native 0.69 上,用户将使用在 react-native
NPM 包的 sdks/hermes-engine/hermes-engine.podspec
文件内定义的 podspec。该 podspec 依赖于 Hermes 的预构建 tarball,我们将其上传到 Maven 和 React Native GitHub Release,作为 React Native 发布过程的一部分(即 查看此版本的资源)。
¥On React Native 0.69, users would instead use a podspec that is defined inside the sdks/hermes-engine/hermes-engine.podspec
file in the react-native
NPM package.
That podspec relies on a pre-built tarball of Hermes that we upload to Maven and to the React Native GitHub Release, as part of the React Native release process (i.e. see the assets of this release).
安卓用户
¥Android Users
在 Android 上,我们将按以下方式更新默认模板中的 android/app/build.gradle
文件:
¥On Android, we're going to update the android/app/build.gradle
file in the default template the following way:
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.aar
和 hermes-release.aar
。
¥Prior to React Native 0.69, users will be consuming hermes-debug.aar
and hermes-release.aar
from the hermes-engine
NPM package.
在 React Native 0.69 上,用户将使用 react-native
NPM 包中 android/com/facebook/react/hermes-engine/
文件夹内提供的 Android 多变体工件。另请注意,我们将完全在 React Native 的未来版本之一中将 删除依赖 放在 hermes-engine
上。
¥On React Native 0.69, users will be consuming the Android multi-variant artifacts available inside the android/com/facebook/react/hermes-engine/
folder in the react-native
NPM package.
Please also note that we're going to remove the dependency on hermes-engine
entirely in one of the future version of React Native.
Android 用户使用新架构
¥Android Users on New Architecture
由于我们的原生代码构建设置的性质(即我们如何使用 NDK),新架构上的用户将从源代码构建 Hermes。
¥Due to the nature of our native code build setup (i.e. how we use the NDK), users on the New Architecture will be building Hermes from source.
这为新架构上的用户调整了 React Native 和 Hermes 的构建机制(他们将从源代码构建这两个框架)。这意味着此类 Android 用户可能会在首次构建时遇到性能下降。
¥This aligns the build mechanism of React Native and Hermes for users on the New Architecture (they will build both framework from source). This means that such Android users might experience a performance hit at build time on their first build.
你可以在此页面上找到优化构建时间并减少对构建的影响的说明:加快构建阶段。
¥You can find instructions to optimize your build time and reduce the impact on your build on this page: Speeding up your Build phase.
Android 用户了解在 Windows 上构建的新架构
¥Android Users on New Architecture building on Windows
在 Windows 计算机上使用新架构构建 React Native 应用的用户需要遵循这些额外步骤才能使构建正常工作:
¥Users building React Native App, with the New Architecture, on Windows machines need to follow those extra steps to let the build work correctly:
-
确保 环境配置正确,带有 Android SDK 和节点。
¥Make sure the environment is configured properly, with Android SDK & node.
-
使用 Chocolatey 安装 cmake
¥Install cmake with Chocolatey
-
安装:
¥Install either:
-
Visual Studio 22 社区版 - 仅选择 C++ 桌面开发就足够了。
¥Visual Studio 22 Community Edition - Picking only the C++ desktop development is sufficient.
-
确保 Visual Studio 命令提示符 配置正确。这是必需的,因为在这些命令提示符中配置了正确的 C++ 编译器环境变量。
¥Make sure the Visual Studio Command Prompt is configured correctly. This is required as the proper C++ compiler environment variable is configured in those command prompt.
-
在 Visual Studio 命令提示符中使用
npx react-native run-android
运行应用。¥Run the app with
npx react-native run-android
inside a Visual Studio Command Prompt.
用户还可以使用其他引擎吗?
¥Can users still use another engine?
是的,用户可以自由启用/禁用 Hermes(Android 上使用 enableHermes
变量,iOS 上使用 hermes_enabled
)。'打包 Hermes' 更改只会影响 Hermes 的构建和打包方式。
¥Yes, users are free to enable/disable Hermes (with the enableHermes
variable on Android, hermes_enabled
on iOS).
The 'Bundled Hermes' change will impact only how Hermes is built and bundled for you.
从 React Native 0.70 开始,enableHermes
/hermes_enabled
默认为 true
。
¥Starting with React Native 0.70, the default for enableHermes
/hermes_enabled
is true
.
这将如何影响贡献者和扩展开发者
¥How this will impact contributor and extension developers
如果你是 React Native 贡献者或者你正在 React Native 或 Hermes 之上构建扩展,请进一步阅读我们解释 Bundled Hermes 的工作原理。
¥If you're a React Native contributor or you're building an extension on top of React Native or Hermes, please read further as we explain how Bundled Hermes works.
打包 Hermes 的幕后工作原理是怎样的?
¥How is Bundled Hermes working under the hood?
此机制依赖于从 facebook/react-native
存储库内的 facebook/hermes
存储库下载包含 Hermes 源代码的 tarball。我们对其他原生依赖(Folly、Glog 等)也有类似的机制,并且我们使 Hermes 遵循相同的设置。
¥This mechanism relies on downloading a tarball with the Hermes source code from the facebook/hermes
repository inside the facebook/react-native
repository. We have a similar mechanism in place for other native dependencies (Folly, Glog, etc.) and we aligned Hermes to follow the same setup.
当从 main
构建 React Native 时,我们将获取 facebook/hermes 的 main
的 tarball 并将其构建为 React Native 构建过程的一部分。
¥When building React Native from main
, we will be fetching a tarball of main
of facebook/hermes and building it as part of the build process of React Native.
当从发布分支(例如 0.69-stable
)构建 React Native 时,我们将使用 Hermes 存储库上的标签来同步两个存储库之间的代码。然后,使用的特定标签名称将存储在 React Native 内发布分支的 sdks/.hermesversion
文件中(例如 0.69 发布分支上的 这是文件)。
¥When building React Native from a release branch (say 0.69-stable
), we will instead use a tag on the Hermes repo to synchronize the code between the two repos. The specific tag name used will then be stored inside the sdks/.hermesversion
file inside React Native in the release branch (e.g. this is the file on the 0.69 release branch).
从某种意义上说,你可以将这种方法视为类似于 git 子模块。
¥In a sense, you can think of this approach similarly to a git submodule.
如果你在 Hermes 之上进行构建,则可以依靠这些标签来了解构建 React Native 时使用的是哪个版本的 Hermes,因为 React Native 的版本在标签名称中指定(例如 hermes-2022-05-20-RNv0.69.0-ee8941b8874132b8f83e4486b63ed5c19fc3f111
)。
¥If you're building on top of Hermes, you can rely on those tags to understand which version of Hermes was used when building React Native, as the version of React Native is specified in the tag name (e.g. hermes-2022-05-20-RNv0.69.0-ee8941b8874132b8f83e4486b63ed5c19fc3f111
).
Android 实现细节
¥Android implementation details
为了在 Android 上实现这一点,我们在 React Native 的 /ReactAndroid/hermes-engine
中添加了一个新的构建,它将负责构建 Hermes 并打包以供使用 (请参阅此处了解更多背景信息)。
¥To implement this on Android, we've added a new build inside the /ReactAndroid/hermes-engine
of React Native that will take care of building Hermes and packaging for consumption (See here for more context).
你现在可以通过调用以下命令来触发 Hermes 引擎的构建:
¥You can now trigger a build of Hermes engine by invoking:
// 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
分支。
¥from the React Native main
branch.
你无需在计算机中安装额外的工具(例如 cmake
、ninja
或 python3
),因为我们将构建配置为使用这些工具的 NDK 版本。
¥You won't need to install extra tools (such as cmake
, ninja
or python3
) in your machine as we configured the build to use the NDK versions of those tools.
在 Gradle 消费者方面,我们还在消费者方面进行了一些小改进:我们从 releaseImplementation
和 debugImplementation
搬到了 implementation
。这是可能的,因为较新的 hermes-engine
Android 工件具有变体感知能力,并且可以将引擎的调试版本与应用的调试版本正确匹配。你在这里不需要任何自定义配置(即使你使用 staging
或其他构建类型/风格)。
¥On the Gradle consumer side, we also shipped a small improvement on the consumer side: we moved from releaseImplementation
& debugImplementation
to implementation
. This is possible because the newer hermes-engine
Android artifact is variant aware and will properly match a debug build of the engine with a debug build of your app. You don't need any custom configuration here (even if you use staging
or other build types/flavors).
然而,这使得模板中必须有这一行:
¥However, this made this line necessary in the template:
exclude group:'com.facebook.fbjni'
这是必需的,因为 React Native 使用非预制方法消耗 fbjni
(即解压缩 .aar
并提取 .so
文件)。Hermes-engine 和其他库正在使用 prefab 来代替 fbjni。我们正在研究未来的 解决这个问题,因此 Hermes 的导入将是单线。
¥This is needed as React Native is consuming fbjni
using the non-prefab approach (i.e. unzipping the .aar
and extracting .so
files). Hermes-engine, and other libraries, are using prefab instead to consume fbjni. We're looking into addressing this issue in the future so the Hermes import will be a oneliner.
iOS 实现细节
¥iOS implementation details
iOS 实现依赖于位于以下位置的一系列脚本:
¥The iOS implementation relies on a series of scripts that lives in the following locations:
-
/scripts/hermes
。这些脚本包含下载 Hermes tarball、解压缩并配置 iOS 构建的逻辑。如果你将hermes_enabled
字段设置为true
,则会在pod install
时间调用它们。¥
/scripts/hermes
. Those scripts contain logic to download the Hermes tarball, unzip it, and configure the iOS build. They're invoked atpod install
time if you have thehermes_enabled
field set totrue
. -
/sdks/hermes-engine
。这些脚本包含有效构建 Hermes 的构建逻辑。它们是从facebook/hermes
存储库复制和改编的,以便在 React Native 中正常工作。具体来说,utils
文件夹内的脚本负责为所有 Mac 平台构建 Hermes。¥
/sdks/hermes-engine
. Those scripts contain the build logic that is effectively building Hermes. They were copied and adapted from thefacebook/hermes
repo to properly work within React Native. Specifically, the scripts inside theutils
folder are responsible of building Hermes for all the Mac platforms.
Hermes 是作为 CircleCI 上 build_hermes_macos
Job 的一部分构建的。该作业将生成一个 tarball 作为工件,当使用已发布的 React Native 版本 (这是 build_hermes_macos
中为 React Native 0.69 创建的工件示例) 时,hermes-engine
podspec 将下载该 tarball。
¥Hermes is built as part of the build_hermes_macos
Job on CircleCI. The job will produce as artifact a tarball which will be downloaded by the hermes-engine
podspec when using a published React Native release (here is an example of the artifacts created for React Native 0.69 in build_hermes_macos
).
预制 Hermes
¥Prebuilt Hermes
如果正在使用的 React Native 版本没有预构建的工件(即你可能正在使用 main
分支的 React Native),则需要从源代码构建 Hermes。首先,Hermes 编译器 hermesc
将在 pod install
期间为 macOS 构建,然后 Hermes 本身将使用 build-hermes-xcode.sh
脚本作为 Xcode 构建管道的一部分构建。
¥If there are no prebuilt artifacts for the React Native version that is being used (i.e. you may be working with React Native from the main
branch), then Hermes will need to be built from source. First, the Hermes compiler, hermesc
, will be built for macOS during pod install
, then Hermes itself will be built as part of the Xcode build pipeline using the build-hermes-xcode.sh
script.
从源代码构建 Hermes
¥Building Hermes from source
当使用 main
分支的 React Native 时,Hermes 始终是从源代码构建的。如果你使用的是稳定的 React Native 版本,则可以在使用 CocoaPods 时通过将 CI
envvar 设置为 true
来强制从源代码构建 Hermes:CI=true pod install
。
¥Hermes is always built from source when using React Native from the main
branch. If you are using a stable React Native version, you can force Hermes to be built from source by setting the CI
envvar to true
when using CocoaPods: CI=true pod install
.
调试符号
¥Debug symbols
默认情况下,Hermes 的预构建工件不包含调试符号 (dSYM)。我们计划在未来为每个版本分发这些调试符号。在那之前,如果你需要 Hermes 的调试符号,则需要从源代码构建 Hermes。将在构建目录中与每个 Hermes 框架一起创建 hermes.framework.dSYM
。
¥The prebuilt artifacts for Hermes do not contain debug symbols (dSYMs) by default. We're planning on distributing these debug symbols for each release in the future. Until then, if you need the debug symbols for Hermes, you will need to build Hermes from source. A hermes.framework.dSYM
will be created in the build directory alongside each of the Hermes frameworks.
恐怕这个变化正在影响我
¥I'm afraid this change is impacting me
我们想强调的是,这本质上是对 Hermes 构建位置以及两个存储库之间代码同步方式的组织变更。更改应该对我们的用户完全透明。
¥We'd like to stress that this is essentially an organizational change on where Hermes is built and how the code is syncronized between the two repositories. The change should be fully transparent to our users.
从历史上看,我们曾经为特定版本的 React Native(例如 v0.11.0 for RN0.68.x
)削减了 Hermes 的版本。
¥Historically, we used to cut a release of Hermes for a specific version of React Native (e.g. v0.11.0 for RN0.68.x
).
使用 '打包 Hermes',你可以依赖一个标签来表示特定版本的 React Native 被删除时所使用的版本。
¥With 'Bundled Hermes', you can instead rely on a tag that will represent the version used when a specific version of React Native was cut.