Builds NativeScript-friendly Hermes artifacts from facebook/hermes commit
10a40b5.
Upstream Hermes ships Node-API itself now, so there is no forward-porting left
to do — Node-API is just a CMake flag. The only local change is
patches/nativescript-hermes.patch, which adds the NativeScript host-object
NAPI extension (napi_create_host_object / napi_get_host_object_data /
napi_is_host_object) behind HERMES_ENABLE_HOST_OBJECT. It compiles into
hermesNapi, so the symbols land in libhermesvm.so / hermes.framework and
there is no extra library to link.
| Flag | Purpose |
|---|---|
HERMES_ENABLE_NAPI |
Node-API layer (upstream, on by default) |
HERMES_ENABLE_HOST_OBJECT |
NativeScript host objects (added by the patch) |
./build_hermes_for_ios_next.shBuilds macOS, iOS device and iOS simulator slices and packages them as
hermes.xcframework with NativeScript-friendly slice naming (the upstream
hermesvm binary is renamed to hermes). Each slice carries the public
headers, and the script also emits sibling hermes-headers and
hermes-tools-macos directories.
Useful overrides:
WORK_ROOT=/tmp/build-hermes \
OUTPUT_XCFRAMEWORK="$PWD/dist/hermes.xcframework" \
OUTPUT_HEADERS_DIR="$PWD/dist/hermes-headers" \
OUTPUT_TOOLS_DIR="$PWD/dist/hermes-tools-macos" \
IOS_DEPLOYMENT_TARGET=13.0 \
MAC_DEPLOYMENT_TARGET=11.0 \
./build_hermes_for_ios_next.sh./build_hermes_for_android.shThe CMake configuration is a 1:1 mirror of upstream's
android/build.gradle.kts — defaultConfig.externalNativeBuild.cmake.arguments
plus the debug / release buildType arguments — so the .so files are the
same ones the AAR would contain. The script drives CMake directly per ABI, so it
runs locally with only the NDK installed (no Gradle, no AGP, no AAR).
Output goes to dist/hermes-android and contains shared libraries only:
<variant>/<abi>/libhermesvm.so
<variant>/<abi>/libjsi.so
debug is CMAKE_BUILD_TYPE=Release plus HERMES_ENABLE_DEBUGGER; release is
MinSizeRel. This matches the layout the NativeScript Android runtime expects
under runtime/src/main/libs/hermes/{debug,release}/<abi>.
The libraries are stripped the way AGP strips them on the way into the AAR — a
freshly linked libhermesvm.so is ~73 MB, ~5.7 MB after --strip-debug
(debug, which also carries the debugger) and ~2.9 MB after --strip-unneeded
(release).
libc++_shared.so is deliberately not shipped — the AAR excludes it too, and the
consuming app supplies it. Headers are identical across platforms and are
published once as hermes-headers.
Two things follow from using the upstream Android config as-is:
HERMES_IS_ANDROID=Truemakes Hermes' CMakeLists requirefbjni(the Java-backed Intl/Unicode platform layer linksfbjni::fbjni). Gradle resolves it from the fbjni AAR through prefab; with no AGP here, the script downloads the same AAR (com.facebook.fbjni:fbjni:0.7.0) and generates the equivalent CMake config package itself. Override withFBJNI_VERSION.- Because
HERMES_ENABLE_INTL=True,libhermesvm.soneedslibfbjni.soat runtime. The AAR excludeslibfbjni.sofor the same reason it excludeslibc++_shared.so: the consuming app is expected to provide it.
Useful overrides:
ANDROID_ABIS="arm64-v8a;x86_64" \
ANDROID_VARIANTS="release" \
ANDROID_API_LEVEL=24 \
ANDROID_NDK_ROOT="$ANDROID_HOME/ndk/27.1.12297006" \
OUTPUT_ANDROID_DIR="$PWD/dist/hermes-android" \
./build_hermes_for_android.shBoth scripts source hermes_common.sh, which owns the upstream commit, the
patch application and the header layout. Overriding UPSTREAM_COMMIT,
HERMES_PATCH, WORK_ROOT, SRC_DIR or JOBS works for either script. The
checkout is reused between runs (git reset --hard + git clean keeping build
directories), so re-running is incremental.
GitHub Actions builds Apple and Android artifacts on macOS, uploads them as
workflow artifacts, and publishes a GitHub release tagged build-<sha12> for
each pushed commit on main:
hermes-xcframework.ziphermes-headers.tar.gzhermes-tools-macos.tar.gzhermes-android.tar.gz