first commit

This commit is contained in:
2026-03-10 16:18:05 +00:00
commit 11f9c069b5
31635 changed files with 3187747 additions and 0 deletions

View File

@@ -0,0 +1,184 @@
# 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.
require "json"
require_relative "./hermes-utils.rb"
begin
react_native_path = File.dirname(Pod::Executable.execute_command('node', ['-p',
'require.resolve(
"react-native",
{paths: [process.argv[1]]},
)', __dir__]).strip
)
rescue => e
# Fallback to the parent directory if the above command fails (e.g when building locally in OOT Platform)
react_native_path = File.join(__dir__, "..", "..")
end
# package.json
package = JSON.parse(File.read(File.join(react_native_path, "package.json")))
versionProperties = Hash[*File.read("version.properties").split(/[=\n]+/)]
if ENV['RCT_HERMES_V1_ENABLED'] == "1"
version = versionProperties['HERMES_V1_VERSION_NAME']
else
version = versionProperties['HERMES_VERSION_NAME']
end
source_type = hermes_source_type(version, react_native_path)
source = podspec_source(source_type, version, react_native_path)
Pod::Spec.new do |spec|
spec.name = "hermes-engine"
spec.version = version
spec.summary = "Hermes is a small and lightweight JavaScript engine optimized for running React Native."
spec.description = "Hermes is a JavaScript engine optimized for fast start-up of React Native apps. It features ahead-of-time static optimization and compact bytecode."
spec.homepage = "https://hermesengine.dev"
spec.license = package['license']
spec.author = "Facebook"
spec.source = source
spec.platforms = { :osx => "10.13", :ios => "15.1", :visionos => "1.0", :tvos => "15.1" }
spec.preserve_paths = '**/*.*'
spec.source_files = ''
spec.pod_target_xcconfig = {
"CLANG_CXX_LANGUAGE_STANDARD" => "c++20",
"CLANG_CXX_LIBRARY" => "compiler-default",
"GCC_WARN_INHIBIT_ALL_WARNINGS" => "YES" # Disable warnings because we don't control this library
}
spec.ios.vendored_frameworks = "destroot/Library/Frameworks/ios/hermesvm.framework"
spec.tvos.vendored_frameworks = "destroot/Library/Frameworks/tvos/hermesvm.framework"
spec.osx.vendored_frameworks = "destroot/Library/Frameworks/macosx/hermesvm.framework"
spec.visionos.vendored_frameworks = "destroot/Library/Frameworks/xros/hermesvm.framework"
if HermesEngineSourceType::isPrebuilt(source_type) then
spec.subspec 'Pre-built' do |ss|
ss.preserve_paths = ["destroot/bin/*"].concat(["**/*.{h,c,cpp}"])
ss.source_files = "destroot/include/hermes/**/*.h"
ss.header_mappings_dir = "destroot/include"
ss.ios.vendored_frameworks = "destroot/Library/Frameworks/universal/hermesvm.xcframework"
ss.visionos.vendored_frameworks = "destroot/Library/Frameworks/universal/hermesvm.xcframework"
ss.tvos.vendored_frameworks = "destroot/Library/Frameworks/universal/hermesvm.xcframework"
ss.osx.vendored_frameworks = "destroot/Library/Frameworks/macosx/hermesvm.framework"
end
# When using the local prebuilt tarball, it should include hermesc compatible with the used VM.
# In other cases, when using Hermes V1, the prebuilt versioned binaries can be used.
if source_type != HermesEngineSourceType::LOCAL_PREBUILT_TARBALL
hermes_compiler_path = File.dirname(Pod::Executable.execute_command('node', ['-p',
"require.resolve(\"hermes-compiler\", {paths: [\"#{react_native_path}\"]})", __dir__]).strip
)
spec.user_target_xcconfig = {
'HERMES_CLI_PATH' => "#{hermes_compiler_path}/hermesc/osx-bin/hermesc"
}
end
# Right now, even reinstalling pods with the PRODUCTION flag turned on, does not change the version of hermes that is downloaded
# To remove the PRODUCTION flag, we want to download the right version of hermes on the flight
# we do so in a pre-build script we invoke from the Xcode build pipeline
# We use this only for Apps created using the template. RNTester and Nightlies should not be used to build for Release.
# We ignore this if we provide a specific tarball: the assumption here is that if you are providing a tarball, is because you want to
# test something specific for that tarball.
if source_type == HermesEngineSourceType::DOWNLOAD_PREBUILD_RELEASE_TARBALL
spec.script_phase = {
:name => "[Hermes] Replace Hermes for the right configuration, if needed",
:execution_position => :before_compile,
:script => <<-EOS
. "$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh"
CONFIG="Release"
if echo $GCC_PREPROCESSOR_DEFINITIONS | grep -q "DEBUG=1"; then
CONFIG="Debug"
fi
"$NODE_BINARY" "$REACT_NATIVE_PATH/sdks/hermes-engine/utils/replace_hermes_version.js" -c "$CONFIG" -r "#{version}" -p "$PODS_ROOT"
EOS
}
end
elsif HermesEngineSourceType::isFromSource(source_type) then
spec.subspec 'Hermes' do |ss|
ss.source_files = ''
ss.public_header_files = 'API/hermes/*.h'
ss.header_dir = 'hermes'
end
spec.subspec 'cdp' do |ss|
ss.source_files = ''
ss.public_header_files = 'API/hermes/cdp/*.h'
ss.header_dir = 'hermes/cdp'
end
spec.subspec 'Public' do |ss|
ss.source_files = ''
ss.public_header_files = 'public/hermes/Public/*.h'
ss.header_dir = 'hermes/Public'
end
if ENV['RCT_HERMES_V1_ENABLED'] != "1"
spec.subspec 'inspector' do |ss|
ss.source_files = ''
ss.public_header_files = 'API/hermes/inspector/*.h'
ss.header_dir = 'hermes/inspector'
end
spec.subspec 'inspector_chrome' do |ss|
ss.source_files = ''
ss.public_header_files = 'API/hermes/inspector/chrome/*.h'
ss.header_dir = 'hermes/inspector/chrome'
end
end
hermesc_path = "${PODS_ROOT}/hermes-engine/build_host_hermesc"
if ENV.has_key?('HERMES_OVERRIDE_HERMESC_PATH') && File.exist?(ENV['HERMES_OVERRIDE_HERMESC_PATH']) then
hermesc_path = ENV['HERMES_OVERRIDE_HERMESC_PATH']
end
spec.user_target_xcconfig = {
'HERMES_CLI_PATH' => "#{hermesc_path}/bin/hermesc"
}
spec.prepare_command = ". '#{react_native_path}/sdks/hermes-engine/utils/create-dummy-hermes-xcframework.sh'"
# This podspec is also run in CI to build Hermes without using Pod install
# and sometimes CI fails because `Pod::Executable` does not exist if it is not run with Pod Install.
if defined?(Pod::Executable.to_s)
CMAKE_BINARY = Pod::Executable::which!('cmake')
# NOTE: Script phases are sorted alphabetically inside Xcode project
spec.script_phases = [
{
:name => '[RN] [1] Build Hermesc',
:output_files => [
"#{hermesc_path}/ImportHostCompilers.cmake"
],
:script => <<-EOS
. "${REACT_NATIVE_PATH}/scripts/xcode/with-environment.sh"
export CMAKE_BINARY=${CMAKE_BINARY:-#{CMAKE_BINARY}}
. ${REACT_NATIVE_PATH}/sdks/hermes-engine/utils/build-hermesc-xcode.sh #{hermesc_path} ${REACT_NATIVE_PATH}/ReactCommon/jsi
EOS
},
{
:name => '[RN] [2] Build Hermes',
:input_files => ["#{hermesc_path}/ImportHostCompilers.cmake"],
:output_files => [
"${PODS_ROOT}/hermes-engine/build/iphonesimulator/API/hermes/hermesvm.framework/hermesvm"
],
:script => <<-EOS
. "${REACT_NATIVE_PATH}/scripts/xcode/with-environment.sh"
export CMAKE_BINARY=${CMAKE_BINARY:-#{CMAKE_BINARY}}
. ${REACT_NATIVE_PATH}/sdks/hermes-engine/utils/build-hermes-xcode.sh #{version} #{hermesc_path}/ImportHostCompilers.cmake ${REACT_NATIVE_PATH}/ReactCommon/jsi
EOS
}
]
end
end
end

View File

@@ -0,0 +1,302 @@
# 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.
require 'net/http'
require 'rexml/document'
HERMES_GITHUB_URL = "https://github.com/facebook/hermes.git"
ENV_BUILD_FROM_SOURCE = "RCT_BUILD_HERMES_FROM_SOURCE"
module HermesEngineSourceType
LOCAL_PREBUILT_TARBALL = :local_prebuilt_tarball
DOWNLOAD_PREBUILD_RELEASE_TARBALL = :download_prebuild_release_tarball
DOWNLOAD_PREBUILT_NIGHTLY_TARBALL = :download_prebuilt_nightly_tarball
BUILD_FROM_GITHUB_COMMIT = :build_from_github_commit
BUILD_FROM_GITHUB_TAG = :build_from_github_tag
BUILD_FROM_GITHUB_MAIN = :build_from_github_main
BUILD_FROM_LOCAL_SOURCE_DIR = :build_from_local_source_dir
def HermesEngineSourceType.isPrebuilt(source_type)
return source_type == LOCAL_PREBUILT_TARBALL || source_type == DOWNLOAD_PREBUILD_RELEASE_TARBALL || source_type == DOWNLOAD_PREBUILT_NIGHTLY_TARBALL
end
def HermesEngineSourceType.isFromSource(source_type)
return source_type == BUILD_FROM_GITHUB_COMMIT || source_type == BUILD_FROM_GITHUB_TAG || source_type == BUILD_FROM_GITHUB_MAIN || source_type == BUILD_FROM_LOCAL_SOURCE_DIR
end
end
# Computes the hermes-engine.podspec's source type.
# - To use a specific tarball, install the dependencies with:
# `HERMES_ENGINE_TARBALL_PATH=<path_to_tarball> bundle exec pod install`
# - To force a build from source, install the dependencies with:
# `RCT_BUILD_HERMES_FROM_SOURCE=true bundle exec pod install`
# If none of the two are provided, Cocoapods will check whether there is a tarball for the current version
# (either release or nightly). If not, it will fall back to building from source (the latest commit on main).
#
# Parameters:
# - version: current version of the pod
# - react_native_path: path to react native
#
# Returns: hermes-engine source type
def hermes_source_type(version, react_native_path)
if override_hermes_dir_envvar_defined()
return HermesEngineSourceType::BUILD_FROM_LOCAL_SOURCE_DIR
end
if hermes_engine_tarball_envvar_defined()
return HermesEngineSourceType::LOCAL_PREBUILT_TARBALL
end
if hermes_commit_envvar_defined()
return HermesEngineSourceType::BUILD_FROM_GITHUB_COMMIT
end
if force_build_from_tag(react_native_path)
return HermesEngineSourceType::BUILD_FROM_GITHUB_TAG
end
if force_build_from_main(react_native_path)
return HermesEngineSourceType::BUILD_FROM_GITHUB_MAIN
end
if release_artifact_exists(version)
return HermesEngineSourceType::DOWNLOAD_PREBUILD_RELEASE_TARBALL
end
if nightly_artifact_exists(version)
return HermesEngineSourceType::DOWNLOAD_PREBUILT_NIGHTLY_TARBALL
end
return HermesEngineSourceType::BUILD_FROM_GITHUB_MAIN
end
def override_hermes_dir_envvar_defined()
return ENV.has_key?('REACT_NATIVE_OVERRIDE_HERMES_DIR')
end
def hermes_engine_tarball_envvar_defined()
return ENV.has_key?('HERMES_ENGINE_TARBALL_PATH')
end
def hermes_commit_envvar_defined()
return ENV.has_key?('HERMES_COMMIT')
end
def hermes_v1_enabled()
return ENV['RCT_HERMES_V1_ENABLED'] == "1"
end
def force_build_from_tag(react_native_path)
return ENV[ENV_BUILD_FROM_SOURCE] === 'true' && File.exist?(hermestag_file(react_native_path))
end
def force_build_from_main(react_native_path)
return ENV[ENV_BUILD_FROM_SOURCE] === 'true' && !File.exist?(hermestag_file(react_native_path))
end
def release_artifact_exists(version)
return hermes_artifact_exists(release_tarball_url(version, :debug))
end
def nightly_artifact_exists(version)
return hermes_artifact_exists(nightly_tarball_url(version).gsub("\\", ""))
end
def podspec_source(source_type, version, react_native_path)
case source_type
when HermesEngineSourceType::BUILD_FROM_LOCAL_SOURCE_DIR
return podspec_source_build_from_local_source_dir(react_native_path)
when HermesEngineSourceType::LOCAL_PREBUILT_TARBALL
return podspec_source_local_prebuilt_tarball()
when HermesEngineSourceType::BUILD_FROM_GITHUB_COMMIT
return podspec_source_build_from_github_commit()
when HermesEngineSourceType::BUILD_FROM_GITHUB_TAG
return podspec_source_build_from_github_tag(react_native_path)
when HermesEngineSourceType::BUILD_FROM_GITHUB_MAIN
return podspec_source_build_from_github_main()
when HermesEngineSourceType::DOWNLOAD_PREBUILD_RELEASE_TARBALL
return podspec_source_download_prebuild_release_tarball(react_native_path, version)
when HermesEngineSourceType::DOWNLOAD_PREBUILT_NIGHTLY_TARBALL
return podspec_source_download_prebuilt_nightly_tarball(version)
else
abort "[Hermes] Unsupported or invalid source type provided: #{source_type}"
end
end
def podspec_source_build_from_local_source_dir(react_native_path)
source_dir_path = ENV['REACT_NATIVE_OVERRIDE_HERMES_DIR']
if Dir.exist?(source_dir_path)
hermes_log("Using source code from local path: #{source_dir_path}")
tarball_dir_path = artifacts_dir()
FileUtils.mkdir_p(tarball_dir_path) unless Dir.exist?(tarball_dir_path)
tarball_path = File.join(tarball_dir_path, "hermes-engine-from-local-source-dir.tar.gz")
exclude_paths = [
"__tests__",
"./external/flowtest",
"./external/esprima/test_fixtures"
]
.map {|path| "--exclude=#{path}"}
.join(' ')
tar_command = "tar #{exclude_paths} -czvf #{tarball_path} -C #{source_dir_path} . 2> /dev/null"
success = system(tar_command)
if !success
abort "Failed to create a tarball with the contents of \"#{source_dir_path}\""
end
return {:http => "file://#{tarball_path}"}
else
abort <<-EOS
[Hermes] REACT_NATIVE_OVERRIDE_HERMES_DIR is set, but points to a non-existing directory: \"#{source_dir_path}\"
If you don't want to use local source, run `unset REACT_NATIVE_OVERRIDE_HERMES_DIR`
EOS
end
end
def podspec_source_local_prebuilt_tarball()
tarball_path = ENV['HERMES_ENGINE_TARBALL_PATH']
if File.exist?(tarball_path)
hermes_log("Using pre-built binary from local path defined by HERMES_ENGINE_TARBALL_PATH envvar: #{tarball_path}")
return {:http => "file://#{tarball_path}"}
end
abort <<-EOS
[Hermes] HERMES_ENGINE_TARBALL_PATH is set, but points to a non-existing file: \"#{tarball_path}\"
If you don't want to use tarball, run `unset HERMES_ENGINE_TARBALL_PATH`
EOS
end
def podspec_source_build_from_github_commit()
commit = ENV['HERMES_COMMIT']
hermes_log("Using commit defined by HERMES_COMMIT envvar: #{commit}")
return {:git => HERMES_GITHUB_URL, :commit => commit}
end
def podspec_source_build_from_github_tag(react_native_path)
tag = File.read(hermestag_file(react_native_path)).strip
if hermes_v1_enabled()
hermes_log("Using tag defined in sdks/.hermesv1version: #{tag}")
else
hermes_log("Using tag defined in sdks/.hermesversion: #{tag}")
end
return {:git => HERMES_GITHUB_URL, :tag => tag}
end
def podspec_source_build_from_github_main()
branch = hermes_v1_enabled() ? "250829098.0.0-stable" : "main"
hermes_log("Using the latest commit from #{branch}.")
return {:git => HERMES_GITHUB_URL, :commit => `git ls-remote #{HERMES_GITHUB_URL} #{branch} | cut -f 1`.strip}
end
def podspec_source_download_prebuild_release_tarball(react_native_path, version)
url = release_tarball_url(version, :debug)
hermes_log("Using release tarball from URL: #{url}")
download_stable_hermes(react_native_path, version, :debug)
download_stable_hermes(react_native_path, version, :release)
return {:http => url}
end
def podspec_source_download_prebuilt_nightly_tarball(version)
url = nightly_tarball_url(version)
hermes_log("Using nightly tarball from URL: #{url}")
return {:http => url}
end
# HELPERS
def artifacts_dir()
return File.join(Pod::Config.instance.project_pods_root, "hermes-engine-artifacts")
end
def hermestag_file(react_native_path)
if hermes_v1_enabled()
return File.join(react_native_path, "sdks", ".hermesv1version")
else
return File.join(react_native_path, "sdks", ".hermesversion")
end
end
def release_tarball_url(version, build_type)
## You can use the `ENTERPRISE_REPOSITORY` variable to customise the base url from which artifacts will be downloaded.
## The mirror's structure must be the same of the Maven repo the react-native core team publishes on Maven Central.
maven_repo_url =
ENV['ENTERPRISE_REPOSITORY'] != nil && ENV['ENTERPRISE_REPOSITORY'] != "" ?
ENV['ENTERPRISE_REPOSITORY'] :
"https://repo1.maven.org/maven2"
namespace = "com/facebook/hermes"
# Sample url from Maven:
# https://repo1.maven.org/maven2/com/facebook/hermes/hermes-ios/0.14.0/hermes-ios-0.14.0-hermes-ios-debug.tar.gz
return "#{maven_repo_url}/#{namespace}/hermes-ios/#{version}/hermes-ios-#{version}-hermes-ios-#{build_type.to_s}.tar.gz"
end
def download_stable_hermes(react_native_path, version, configuration)
tarball_url = release_tarball_url(version, configuration)
download_hermes_tarball(react_native_path, tarball_url, version, configuration)
end
def download_hermes_tarball(react_native_path, tarball_url, version, configuration)
destination_path = configuration == nil ?
"#{artifacts_dir()}/hermes-ios-#{version}.tar.gz" :
"#{artifacts_dir()}/hermes-ios-#{version}-#{configuration}.tar.gz"
unless File.exist?(destination_path)
# Download to a temporary file first so we don't cache incomplete downloads.
tmp_file = "#{artifacts_dir()}/hermes-ios.download"
`mkdir -p "#{artifacts_dir()}" && curl "#{tarball_url}" -Lo "#{tmp_file}" && mv "#{tmp_file}" "#{destination_path}"`
end
return destination_path
end
def nightly_tarball_url(version)
artifact_coordinate = "hermes-ios"
artifact_name = "hermes-ios-debug.tar.gz"
namespace = "com/facebook/hermes"
xml_url = "https://central.sonatype.com/repository/maven-snapshots/#{namespace}/#{artifact_coordinate}/#{version}-SNAPSHOT/maven-metadata.xml"
response = Net::HTTP.get_response(URI(xml_url))
if response.is_a?(Net::HTTPSuccess)
xml = REXML::Document.new(response.body)
timestamp = xml.elements['metadata/versioning/snapshot/timestamp'].text
build_number = xml.elements['metadata/versioning/snapshot/buildNumber'].text
full_version = "#{version}-#{timestamp}-#{build_number}"
final_url = "https://central.sonatype.com/repository/maven-snapshots/#{namespace}/#{artifact_coordinate}/#{version}-SNAPSHOT/#{artifact_coordinate}-#{full_version}-#{artifact_name}"
return final_url
else
return ""
end
end
def resolve_url_redirects(url)
return (`curl -Ls -o /dev/null -w %{url_effective} \"#{url}\"`)
end
# This function checks that Hermes artifact exists.
# As of now it should check it on the Maven repo.
#
# Parameters
# - version: the version of React Native
# - build_type: debug or release
def hermes_artifact_exists(tarball_url)
# -L is used to follow redirects, useful for the nightlies
# I also needed to wrap the url in quotes to avoid escaping & and ?.
return (`curl -o /dev/null --silent -Iw '%{http_code}' -L "#{tarball_url}"` == "200")
end
def hermes_log(message, level = :warning)
if !Object.const_defined?("Pod::UI")
return
end
hermes_log_messgae = '[Hermes] ' + message
case level
when :info
Pod::UI.puts hermes_log_messgae.green
when :error
Pod::UI.puts hermes_log_messgae.red
else
Pod::UI.puts hermes_log_messgae.yellow
end
end

View File

@@ -0,0 +1,234 @@
#!/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.
# Defines functions for building various Hermes frameworks.
# See build-ios-framework.sh and build-mac-framework.sh for usage examples.
CURR_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
IMPORT_HOST_COMPILERS_PATH=${HERMES_OVERRIDE_HERMESC_PATH:-$PWD/build_host_hermesc/ImportHostCompilers.cmake}
BUILD_TYPE=${BUILD_TYPE:-Debug}
HERMES_PATH="$CURR_SCRIPT_DIR/.."
REACT_NATIVE_PATH=${REACT_NATIVE_PATH:-$CURR_SCRIPT_DIR/../../..}
NUM_CORES=$(sysctl -n hw.ncpu)
PLATFORMS=("macosx" "iphoneos" "iphonesimulator" "catalyst" "xros" "xrsimulator" "appletvos" "appletvsimulator")
if [[ -z "$JSI_PATH" ]]; then
JSI_PATH="$REACT_NATIVE_PATH/ReactCommon/jsi"
fi
function use_env_var_or_ruby_prop {
if [[ -n "$1" ]]; then
echo "$1"
else
ruby -rcocoapods-core -rjson -e "puts Pod::Specification.from_file('hermes-engine.podspec').$2"
fi
}
function use_env_var {
if [[ -n "$1" ]]; then
echo "$1"
else
echo "error: Missing $2 environment variable"
exit 1
fi
}
function get_release_version {
use_env_var_or_ruby_prop "${RELEASE_VERSION}" "version"
}
function get_ios_deployment_target {
use_env_var "${IOS_DEPLOYMENT_TARGET}" "IOS_DEPLOYMENT_TARGET"
}
function get_visionos_deployment_target {
use_env_var "${XROS_DEPLOYMENT_TARGET}" "XROS_DEPLOYMENT_TARGET"
}
function get_mac_deployment_target {
use_env_var "${MAC_DEPLOYMENT_TARGET}" "MAC_DEPLOYMENT_TARGET"
}
# Build host hermes compiler for internal bytecode
function build_host_hermesc {
echo "Building hermesc"
pushd "$HERMES_PATH" > /dev/null || exit 1
cmake -S . -B build_host_hermesc -DJSI_DIR="$JSI_PATH"
cmake --build ./build_host_hermesc --target hermesc -j "${NUM_CORES}"
popd > /dev/null || exit 1
}
# Utility function to configure an Apple framework
function configure_apple_framework {
local enable_debugger cmake_build_type xcode_15_flags xcode_major_version
if [[ $BUILD_TYPE == "Debug" ]]; then
enable_debugger="true"
else
enable_debugger="false"
fi
if [[ $BUILD_TYPE == "Debug" ]]; then
# JS developers aren't VM developers.
# Therefore we're passing as build type Release, to provide a faster build.
cmake_build_type="Release"
else
cmake_build_type="MinSizeRel"
fi
xcode_15_flags=""
xcode_major_version=$(xcodebuild -version | grep -oE '[0-9]*' | head -n 1)
if [[ $xcode_major_version -ge 15 ]]; then
xcode_15_flags="LINKER:-ld_classic"
fi
boost_context_flag=""
if [[ $1 == "catalyst" ]]; then
boost_context_flag="-DHERMES_ALLOW_BOOST_CONTEXT=0"
fi
pushd "$HERMES_PATH" > /dev/null || exit 1
cmake -S . -B "build_$1" \
-DHERMES_EXTRA_LINKER_FLAGS="$xcode_15_flags" \
-DHERMES_APPLE_TARGET_PLATFORM:STRING="$1" \
-DCMAKE_OSX_ARCHITECTURES:STRING="$2" \
-DCMAKE_OSX_DEPLOYMENT_TARGET:STRING="$3" \
-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_SHARED_JSI:BOOLEAN=false \
-DCMAKE_CXX_FLAGS:STRING="-gdwarf" \
-DCMAKE_C_FLAGS:STRING="-gdwarf" \
-DIMPORT_HOST_COMPILERS:PATH="$IMPORT_HOST_COMPILERS_PATH" \
-DJSI_DIR="$JSI_PATH" \
-DHERMES_RELEASE_VERSION="for RN $(get_release_version)" \
-DCMAKE_BUILD_TYPE="$cmake_build_type" \
$boost_context_flag
popd > /dev/null || exit 1
}
function build_host_hermesc_if_needed {
if [[ ! -f "$IMPORT_HOST_COMPILERS_PATH" ]]; then
build_host_hermesc
else
echo "[HermesC] Skipping! Found an existent hermesc already at: $IMPORT_HOST_COMPILERS_PATH"
fi
}
# Utility function to build an Apple framework
function build_apple_framework {
# Only build host HermesC if no file found at $IMPORT_HOST_COMPILERS_PATH
build_host_hermesc_if_needed
# Confirm ImportHostCompilers.cmake is now available.
[ ! -f "$IMPORT_HOST_COMPILERS_PATH" ] &&
echo "Host hermesc is required to build apple frameworks!"
# $1: platform, $2: architectures, $3: deployment target
echo "Building $BUILD_TYPE framework for $1 with architectures: $2"
configure_apple_framework "$1" "$2" "$3"
pushd "$HERMES_PATH" > /dev/null || exit 1
mkdir -p "destroot/Library/Frameworks/$1"
cmake --build "./build_$1" --target hermesvm -j "${NUM_CORES}"
# Produce the dSYM.
xcrun dsymutil "./build_$1/lib/hermesvm.framework/hermesvm" -o "./build_$1/lib/hermesvm.framework.dSYM"
cp -R "./build_$1"/lib/hermesvm.framework* "destroot/Library/Frameworks/$1"
# In a MacOS build, also produce the hermes and hermesc CLI tools.
if [[ $1 == macosx ]]; then
cmake --build "./build_$1" --target hermesc hermes -j "${NUM_CORES}"
mkdir -p destroot/bin
cp "./build_$1/bin"/* "destroot/bin"
fi
# Copy over Hermes and JSI API headers.
mkdir -p destroot/include/hermes/Public
cp public/hermes/Public/*.h destroot/include/hermes/Public
mkdir -p destroot/include/hermes
cp API/hermes/*.h destroot/include/hermes
mkdir -p destroot/include/hermes/cdp
cp API/hermes/cdp/*.h destroot/include/hermes/cdp
mkdir -p destroot/include/hermes/inspector
cp API/hermes/inspector/*.h destroot/include/hermes/inspector
mkdir -p destroot/include/hermes/inspector/chrome
cp API/hermes/inspector/chrome/*.h destroot/include/hermes/inspector/chrome
mkdir -p destroot/include/jsi
cp "$JSI_PATH"/jsi/*.h destroot/include/jsi
popd > /dev/null || exit 1
}
function prepare_dest_root_for_ci {
mkdir -p "destroot/bin"
for platform in "${PLATFORMS[@]}"; do
mkdir -p "destroot/Library/Frameworks/$platform"
cp -R "./build_$platform/lib/hermesvm.framework"* "destroot/Library/Frameworks/$platform"
done
cp "./build_macosx/bin/"* "destroot/bin"
# Copy over Hermes and JSI API headers.
mkdir -p destroot/include/hermes/Public
cp public/hermes/Public/*.h destroot/include/hermes/Public
mkdir -p destroot/include/hermes
cp API/hermes/*.h destroot/include/hermes
mkdir -p destroot/include/hermes/cdp
cp API/hermes/cdp/*.h destroot/include/hermes/cdp
mkdir -p destroot/include/hermes/inspector
cp API/hermes/inspector/*.h destroot/include/hermes/inspector
mkdir -p destroot/include/hermes/inspector/chrome
cp API/hermes/inspector/chrome/*.h destroot/include/hermes/inspector/chrome
mkdir -p destroot/include/jsi
cp "$JSI_PATH"/jsi/*.h destroot/include/jsi
}
# Accepts an array of frameworks and will place all of
# the architectures into an universal folder and then remove
# the merged frameworks from destroot
function create_universal_framework {
pushd "$HERMES_PATH/destroot/Library/Frameworks" > /dev/null || exit 1
local platforms=("$@")
local args=""
echo "Creating universal framework for platforms: ${platforms[*]}"
for i in "${!platforms[@]}"; do
local platform="${platforms[$i]}"
local hermes_framework_path="${platform}/hermesvm.framework"
args+="-framework $hermes_framework_path "
done
mkdir -p universal
# shellcheck disable=SC2086
if xcodebuild -create-xcframework $args -output "universal/hermesvm.xcframework"
then
# # Remove the thin iOS hermesvm.frameworks that are now part of the universal
# XCFramework
for platform in "${platforms[@]}"; do
rm -r "$platform"
done
fi
popd > /dev/null || exit 1
}

View File

@@ -0,0 +1,108 @@
#!/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 -e
release_version="$1"; shift
hermesc_path="$1"; shift
jsi_path="$1"; shift
# Based on platform name returns the framework copy destination. Used later by `vendored_frameworks` in Podspec.
# Fallbacks to "ios" if platform is not recognized.
function get_platform_copy_destination {
if [[ $1 == "macosx" ]]; then
echo "macosx"
return
elif [[ $1 == "xros" || $1 == "xrsimulator" ]]; then
echo "xros"
return
fi
echo "ios"
}
function get_deployment_target {
if [[ $1 == "macosx" ]]; then
echo "${MACOSX_DEPLOYMENT_TARGET}"
return
elif [[ $1 == "xrsimulator" || $1 == "xros" ]]; then
echo "${XROS_DEPLOYMENT_TARGET}"
return
fi
echo "${IPHONEOS_DEPLOYMENT_TARGET}"
}
enable_debugger="false"
if [[ "$CONFIGURATION" = *Debug* ]]; then
enable_debugger="true"
fi
cmake_build_type=""
if [[ "$CONFIGURATION" = *Debug* ]]; then
# JS developers aren't VM developers.
# Therefore we're passing as build type Release, to provide a faster build.
cmake_build_type="Release"
else
cmake_build_type="MinSizeRel"
fi
deployment_target=$(get_deployment_target $PLATFORM_NAME)
xcode_15_flags=""
xcode_major_version=$(xcodebuild -version | grep -oE '[0-9]*' | head -n 1)
if [[ $xcode_major_version -ge 15 ]]; then
echo "########### Using LINKER:-ld_classic ###########"
xcode_15_flags="LINKER:-ld_classic"
fi
boost_context_flag=""
if [[ $PLATFORM_NAME == "catalyst" ]]; then
boost_context_flag="-DHERMES_ALLOW_BOOST_CONTEXT=0"
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_EXTRA_LINKER_FLAGS="$xcode_15_flags" \
-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_SHARED_JSI:BOOLEAN=false \
-DCMAKE_CXX_FLAGS:STRING="-gdwarf" \
-DCMAKE_C_FLAGS:STRING="-gdwarf" \
-DIMPORT_HOST_COMPILERS:PATH="${hermesc_path}" \
-DJSI_DIR="$jsi_path" \
-DHERMES_RELEASE_VERSION="for RN $release_version" \
-DCMAKE_BUILD_TYPE="$cmake_build_type" \
$boost_context_flag
echo "Build Apple framework"
"$CMAKE_BINARY" \
--build "${PODS_ROOT}/hermes-engine/build/${PLATFORM_NAME}" \
--target hermesvm \
-j "$(sysctl -n hw.ncpu)"
echo "Copy Apple framework to destroot/Library/Frameworks"
platform_copy_destination=$(get_platform_copy_destination $PLATFORM_NAME)
mkdir -p "${PODS_ROOT}/hermes-engine/destroot/Library/Frameworks/${platform_copy_destination}"
cp -pfR \
"${PODS_ROOT}/hermes-engine/build/${PLATFORM_NAME}/lib/hermesvm.framework" \
"${PODS_ROOT}/hermes-engine/destroot/Library/Frameworks/${platform_copy_destination}"

View File

@@ -0,0 +1,26 @@
#!/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 -e
hermesc_dir_path="$1"; shift
jsi_path="$1"
# This script is supposed to be executed from Xcode "run script" phase.
# Xcode sets up its build environment based on the build target (iphone, iphonesimulator, macosx).
# We want to make sure that hermesc is built for mac.
# So we clean the environment with env -i, and explicitly set SDKROOT to macosx
SDKROOT=$(xcode-select -p)/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk
env -i \
PATH="$PATH" \
SDKROOT="$SDKROOT" \
"$CMAKE_BINARY" -S "${PODS_ROOT}/hermes-engine" -B "$hermesc_dir_path" -DJSI_DIR="$jsi_path" -DCMAKE_BUILD_TYPE=Release
env -i \
PATH="$PATH" \
SDKROOT="$SDKROOT" \
"$CMAKE_BINARY" --build "$hermesc_dir_path" --target hermesc -j "$(sysctl -n hw.ncpu)"

View File

@@ -0,0 +1,90 @@
#!/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.
if [ "$CI" ]; then
set -x
fi
set -e
# Given a specific target, retrieve the right architecture for it
# $1 the target you want to build. Allowed values: iphoneos, iphonesimulator, catalyst, xros, xrsimulator
function get_architecture {
if [[ $1 == "iphoneos" || $1 == "xros" ]]; then
echo "arm64"
elif [[ $1 == "iphonesimulator" || $1 == "xrsimulator" ]]; then
echo "x86_64;arm64"
elif [[ $1 == "appletvos" ]]; then
echo "arm64"
elif [[ $1 == "appletvsimulator" ]]; then
echo "x86_64;arm64"
elif [[ $1 == "catalyst" ]]; then
echo "x86_64;arm64"
else
echo "Error: unknown architecture passed $1"
exit 1
fi
}
function get_deployment_target {
if [[ $1 == "xros" || $1 == "xrsimulator" ]]; then
echo "$(get_visionos_deployment_target)"
else # tvOS and iOS use the same deployment target
echo "$(get_ios_deployment_target)"
fi
}
# build a single framework
# $1 is the target to build
function build_framework {
if [ ! -d destroot/Library/Frameworks/universal/hermesvm.xcframework ]; then
deployment_target=$(get_deployment_target "$1")
architecture=$(get_architecture "$1")
build_apple_framework "$1" "$architecture" "$deployment_target"
else
echo "Skipping; Clean \"destroot\" to rebuild".
fi
}
# group the frameworks together to create a universal framework
function build_universal_framework {
if [ ! -d destroot/Library/Frameworks/universal/hermesvm.xcframework ]; then
create_universal_framework "iphoneos" "iphonesimulator" "catalyst" "xros" "xrsimulator" "appletvos" "appletvsimulator"
else
echo "Skipping; Clean \"destroot\" to rebuild".
fi
}
# single function that builds sequentially iphoneos, iphonesimulator and catalyst
# this is used to preserve backward compatibility
function create_framework {
if [ ! -d destroot/Library/Frameworks/universal/hermesvm.xcframework ]; then
build_framework "iphoneos"
build_framework "iphonesimulator"
build_framework "appletvos"
build_framework "appletvsimulator"
build_framework "catalyst"
build_framework "xros"
build_framework "xrsimulator"
build_universal_framework
else
echo "Skipping; Clean \"destroot\" to rebuild".
fi
}
CURR_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
# shellcheck source=xplat/js/react-native-github/sdks/hermes-engine/utils/build-apple-framework.sh
. "${CURR_SCRIPT_DIR}/build-apple-framework.sh"
if [[ -z $1 ]]; then
create_framework
elif [[ $1 == "build_framework" ]]; then
build_universal_framework
else
build_framework "$1"
fi

View File

@@ -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.
if [ "$CI" ]; then
set -x
fi
set -e
# shellcheck source=xplat/js/react-native-github/sdks/hermes-engine/utils/build-apple-framework.sh
CURR_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
. "${CURR_SCRIPT_DIR}/build-apple-framework.sh"
if [ ! -d destroot/Library/Frameworks/macosx/hermesvm.framework ]; then
mac_deployment_target=$(get_mac_deployment_target)
build_apple_framework "macosx" "x86_64;arm64" "$mac_deployment_target"
else
echo "Skipping; Clean \"destroot\" to rebuild".
fi

View File

@@ -0,0 +1,7 @@
#!/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.
echo "This file is no-op now. Remove \"[RN] Copy Hermes Framework\" script phase from your main target if you don't want to see this message."

View File

@@ -0,0 +1,32 @@
#!/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
# CocoaPods requires vendored frameworks to exist before `pod install` is run,
# and to be proper Moch-O binaries in order to auto-link them to the user's Xcode project.
# This script creates dummy hermesvm.framework for macosx and ios.
# They are then get rewritten by `build-hermes-xcode.sh` during Xcode build.
rm -rf destroot
mkdir -p destroot/Library/Frameworks
pushd destroot/Library/Frameworks > /dev/null || exit 1
echo '' > dummy.c
platforms=( "macosx" "ios" "xros" ) # Add other platforms here if needed
for platform in "${platforms[@]}"
do
mkdir -p "${platform}/hermesvm.framework"
clang dummy.c -dynamiclib -o "${platform}/hermesvm.framework/hermesvm"
done
rm dummy.c
popd > /dev/null || exit 1

View File

@@ -0,0 +1,109 @@
/**
* 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.
*
* @flow strict-local
* @format
*/
'use strict';
const {spawnSync} = require('child_process');
const fs = require('fs');
const yargs = require('yargs');
const LAST_BUILD_FILENAME = '.last_build_configuration';
function validateBuildConfiguration(configuration) {
if (!['Debug', 'Release'].includes(configuration)) {
throw new Error(`Invalid configuration ${configuration}`);
}
}
function validateVersion(version) {
if (version == null || version === '') {
throw new Error('Version cannot be empty');
}
}
function shouldReplaceHermesConfiguration(configuration) {
const fileExists = fs.existsSync(LAST_BUILD_FILENAME);
if (fileExists) {
console.log(`Found ${LAST_BUILD_FILENAME} file`);
const oldConfiguration = fs.readFileSync(LAST_BUILD_FILENAME).toString();
if (oldConfiguration === configuration) {
console.log(
'Same config of the previous build. No need to replace Hermes engine',
);
return false;
}
}
// Assumption: if there is no stored last build, we assume that it was build for debug.
if (!fileExists && configuration === 'Debug') {
console.log(
'No previous build detected, but Debug Configuration. No need to replace Hermes engine',
);
return false;
}
return true;
}
function replaceHermesConfiguration(configuration, version, podsRoot) {
const tarballURLPath = `${podsRoot}/hermes-engine-artifacts/hermes-ios-${version.toLowerCase()}-${configuration.toLowerCase()}.tar.gz`;
const finalLocation = 'hermes-engine';
console.log('Preparing the final location');
fs.rmSync(finalLocation, {force: true, recursive: true});
fs.mkdirSync(finalLocation, {recursive: true});
console.log('Extracting the tarball');
spawnSync('tar', ['-xf', tarballURLPath, '-C', finalLocation], {
stdio: 'inherit',
});
}
function updateLastBuildConfiguration(configuration) {
fs.writeFileSync(LAST_BUILD_FILENAME, configuration);
}
function main(configuration, version, podsRoot) {
validateBuildConfiguration(configuration);
validateVersion(version);
if (!shouldReplaceHermesConfiguration(configuration)) {
return;
}
replaceHermesConfiguration(configuration, version, podsRoot);
updateLastBuildConfiguration(configuration);
console.log('Done replacing hermes-engine');
}
// This script is executed in the Pods folder, which is usually not synched to Github, so it should be ok
const argv = yargs
.option('c', {
alias: 'configuration',
description:
'Configuration to use to download the right Hermes version. Allowed values are "Debug" and "Release".',
})
.option('r', {
alias: 'reactNativeVersion',
description:
'The Version of React Native associated with the Hermes tarball.',
})
.option('p', {
alias: 'podsRoot',
description: 'The path to the Pods root folder',
})
.usage('Usage: $0 -c Debug -r <version> -p <path/to/react-native>').argv;
const configuration = argv.configuration;
const version = argv.reactNativeVersion;
const podsRoot = argv.podsRoot;
main(configuration, version, podsRoot);

View File

@@ -0,0 +1,2 @@
HERMES_VERSION_NAME=0.14.1
HERMES_V1_VERSION_NAME=250829098.0.4