-
Notifications
You must be signed in to change notification settings - Fork 24.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Integrate Hermes with the Xcode build process
Summary: ## Context If React Native is built from *main* of any non-stable commit, then Hermes is built from source. The build is performed by `build-ios-framework.sh` and `build-mac-framework.sh` scripts in `hermes-engine.podspec` `prepare_command` stage. Since those scripts have no access build target information, they build all possible architectures and platforms just in case. That takes ages. ## Solution The idea is to integrate build script into Xcode *run script* phase, and use build target information to build Hermes for active architecture only. ## Implementation - Existing behaviour remains unchanged for local tarball and remote prebuild cases. - `build-hermesc-xcode.sh` builds Hermesc as `hermes-engine.podspec` `prepare_command`. Default build location is `react-native/sdks/hermes-engine/build_host_hermesc`. - `build-hermes-xcode.sh` builds Hermes in 'Build Hermes' Xcode script phase. It uses `$PLATFORM_NAME`, `$CONFIGURATION`, `$ARCHS`, `$IPHONEOS_DEPLOYMENT_TARGET` and `$MACOSX_DEPLOYMENT_TARGET` environment variables to configure cmake project so it builds only active architecture. The script also gets RN version, *cmake* path and *hermesc* path from the podspec. - `copy-hermes-xcode.sh` copies Hermes.framework inside the app bundle. This script phase is added to the user app target in a `post_install` hook, after pods are integrated in a user project. - `OTHER_LDFLAGS -framework "hermes"` added to the user project to enable linking against Hermes.framework. - If `HERMES_OVERRIDE_HERMESC_PATH` is set, then Hermesc building is skipped, and `HERMES_OVERRIDE_HERMESC_PATH` is used for `build-hermes-xcode.sh`. - `HERMES_CLI_PATH` is injected into user project config to enable Hermes source maps in `react-native-xcode.sh`. ## Things that didn't work - *Running build-hermesc-xcode.sh in Xcode run script phase*. This doesn't work because Hermesc is supposed to be built for macos, and if build target is ios, then Xcode configures environment in such a way that Hermesc build fails. - *Installing Hermesc into CocoaPods download folder*. So it then ends up in `Pods/hermes-engine/build_host_hermesc`, and all the housekeeping is handled by CocoaPods. This doesn't work because cmake uses absolute paths in a configured project. If configured project is moved to a different location, nothing builds. - *Installing Hermesc directly into Pods/hermes-engine*. This doesn't work because CocoaPods runs prepare_command before Pods folder clean up, and everything gets wiped. ## Known issue - If `Pods/hermes-engine` is manually removed, then `sdks/hermes-engine/build_host_hermesc` must also be removed before running `pod install`. Otherwise cmake will complain about stale cache: ``` CMake Error: The source "<CocoaPodsCache>/hermes-engine/<hash2>/CMakeLists.txt" does not match the source "<CocoaPodsCache>/hermes-engine/<has1>/CMakeLists.txt" used to generate cache. Re-run cmake with a different source directory. ``` ## Benchmark MacBook M1 2021 32 GB. ``` export REACT_NATIVE_PATH=~/fbsource/xplat/js/react-native-github cd $REACT_NATIVE_PATH/packages/rn-tester pod install rm -rf $REACT_NATIVE_PATH/sdks/hermes-engine/build_host_hermesc cd $REACT_NATIVE_PATH/packages/rn-tester/Pods/hermes-engine echo 't1=$(date +%s); $@; t2=$(date +%s); diff=$(echo "$t2 - $t1" | bc); echo Operation took $diff seconds.' > /tmp/benchmark.sh ``` ``` # Before export BUILD_TYPE=Debug export JSI_PATH=$REACT_NATIVE_PATH/ReactCommon/jsi export RELEASE_VERSION=1000.0 export IOS_DEPLOYMENT_TARGET=iphonesimulator export MAC_DEPLOYMENT_TARGET=12.6 cd $REACT_NATIVE_PATH/packages/rn-tester/Pods/hermes-engine . /tmp/benchmark.sh $REACT_NATIVE_PATH/sdks/hermes-engine/utils/build-ios-framework.sh # Operation took 252 seconds . /tmp/benchmark.sh $REACT_NATIVE_PATH/sdks/hermes-engine/utils/build-mac-framework.sh # Operation took 179 seconds ``` ``` # After . /tmp/benchmark.sh source $REACT_NATIVE_PATH/sdks/hermes-engine/utils/build-hermesc-xcode.sh $REACT_NATIVE_PATH/sdks/hermes-engine/build_host_hermesc # Operation took 59 seconds. . /tmp/benchmark.sh xcodebuild -workspace $REACT_NATIVE_PATH/packages/rn-tester/RNTesterPods.xcworkspace -scheme hermes-engine # Operation took 106 seconds. ``` |Before|||After||| |--| |iOS framework (s)|Mac framework (s)|Total (s)|Hermesc (s)|Target-specific framework (s)|Total (s)| |252|179|431|59|106|**165 (-266) (-61%)**| The performance win is fixed, and does not depend on the project size and structure. As an example, this is how these changes affect build time of RNTester. |Before||||After||| |--| ||Pod install (s)|Xcode build (s)|Total (s)|Pod install (s)|Xcode build (s)|Total (s)| |Clean build|1219|132|1352|734 (-485)|249(+117)|**983 (-369)**| |Incremental build|82|30|112|105 (+23)|**34 (+4)**|139 (+27)| The most important values here are the total clean build time and the incremental Xcode build time. The first one went down by 369 seconds, the second one went up by 4 seconds. I consider it a reasonable tradeoff. The extra 4 seconds in the incremental Xcode build time can potentially be mitigated by setting up output file lists for the new script phases. allow-large-files Changelog: [iOS][Changed] - Hermes is integrated into Xcode build. Reviewed By: hramos Differential Revision: D40063686 fbshipit-source-id: e6993d62225789377db769244bc07786cc978a27
- Loading branch information
1 parent
f49b251
commit 6b8e13f
Showing
9 changed files
with
200 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
#!/bin/bash | ||
# Copyright (c) Meta Platforms, Inc. and affiliates. | ||
# | ||
# This source code is licensed under the MIT license found in the | ||
# LICENSE file in the root directory of this source tree. | ||
|
||
set -x | ||
|
||
release_version="$1"; shift | ||
hermesc_path="$1"; shift | ||
|
||
build_cli_tools="false" | ||
if [[ "$PLATFORM_NAME" == macosx ]]; then | ||
build_cli_tools="true" | ||
fi | ||
|
||
enable_debugger="false" | ||
if [[ "$CONFIGURATION" == "Debug" ]]; then | ||
enable_debugger="true" | ||
fi | ||
|
||
deployment_target=${IPHONEOS_DEPLOYMENT_TARGET} | ||
if [ -z "$deployment_target" ]; then | ||
deployment_target=${MACOSX_DEPLOYMENT_TARGET} | ||
fi | ||
|
||
architectures=$( echo "$ARCHS" | tr " " ";" ) | ||
|
||
echo "Configure Apple framework" | ||
|
||
"$CMAKE_BINARY" \ | ||
-S "${PODS_ROOT}/hermes-engine" \ | ||
-B "${PODS_ROOT}/hermes-engine/build/${PLATFORM_NAME}" \ | ||
-DHERMES_APPLE_TARGET_PLATFORM:STRING="$PLATFORM_NAME" \ | ||
-DCMAKE_OSX_ARCHITECTURES:STRING="$architectures" \ | ||
-DCMAKE_OSX_DEPLOYMENT_TARGET:STRING="$deployment_target" \ | ||
-DHERMES_ENABLE_DEBUGGER:BOOLEAN="$enable_debugger" \ | ||
-DHERMES_ENABLE_INTL:BOOLEAN=true \ | ||
-DHERMES_ENABLE_LIBFUZZER:BOOLEAN=false \ | ||
-DHERMES_ENABLE_FUZZILLI:BOOLEAN=false \ | ||
-DHERMES_ENABLE_TEST_SUITE:BOOLEAN=false \ | ||
-DHERMES_ENABLE_BITCODE:BOOLEAN=false \ | ||
-DHERMES_BUILD_APPLE_FRAMEWORK:BOOLEAN=true \ | ||
-DHERMES_BUILD_APPLE_DSYM:BOOLEAN=true \ | ||
-DHERMES_ENABLE_TOOLS:BOOLEAN="$build_cli_tools" \ | ||
-DIMPORT_HERMESC:PATH="${hermesc_path}" \ | ||
-DHERMES_RELEASE_VERSION="for RN $release_version" \ | ||
-DCMAKE_INSTALL_PREFIX:PATH="${PODS_ROOT}/hermes-engine/destroot" \ | ||
-DCMAKE_BUILD_TYPE="$CONFIGURATION" | ||
|
||
echo "Build Apple framework" | ||
|
||
"$CMAKE_BINARY" \ | ||
--build "${PODS_ROOT}/hermes-engine/build/${PLATFORM_NAME}" \ | ||
--target "install/strip" \ | ||
-j "$(sysctl -n hw.ncpu)" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
#!/bin/bash | ||
# Copyright (c) Meta Platforms, Inc. and affiliates. | ||
# | ||
# This source code is licensed under the MIT license found in the | ||
# LICENSE file in the root directory of this source tree. | ||
|
||
set -x | ||
|
||
hermesc_dir_path="$1" | ||
|
||
CMAKE_BINARY=${CMAKE_BINARY:-cmake} | ||
|
||
if ! "$CMAKE_BINARY" -S . -B "$hermesc_dir_path" | ||
then | ||
echo "Failed to configure Hermesc cmake project." | ||
exit 1 | ||
fi | ||
if ! "$CMAKE_BINARY" --build "$hermesc_dir_path" --target hermesc -j "$(sysctl -n hw.ncpu)" | ||
then | ||
echo "Failed to build Hermesc cmake project." | ||
exit 1 | ||
fi |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
#!/bin/bash | ||
# Copyright (c) Meta Platforms, Inc. and affiliates. | ||
# | ||
# This source code is licensed under the MIT license found in the | ||
# LICENSE file in the root directory of this source tree. | ||
|
||
set -x | ||
|
||
source="${PODS_ROOT}/hermes-engine/destroot/Library/Frameworks/${PLATFORM_NAME}/hermes.framework" | ||
|
||
if [[ ! -f "$source" ]]; then | ||
cp -R "$source" "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes/hermes.framework" | ||
cp -R "$source" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" | ||
fi |