From cb9eaa6db0336fd120eb7b46f96c33cc116eedcb Mon Sep 17 00:00:00 2001 From: Riccardo Cipolleschi Date: Thu, 5 Jan 2023 06:58:38 -0800 Subject: [PATCH] Refactor hermes logic to make it branch agnostic (#35709) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/35709 This change refactors the logic to choose which version of hermes we have to pick ## Changelog: [iOS][Changed] - Refactor hermes choosing logic Reviewed By: cortinico, dmytrorykun Differential Revision: D42211405 fbshipit-source-id: d19c0f2c523c5596d18a1f904e3b26d96ea1a77a --- .circleci/config.yml | 3 + sdks/hermes-engine/hermes-engine.podspec | 35 +------ sdks/hermes-engine/hermes-utils.rb | 118 ++++++++++++++++++++++- 3 files changed, 122 insertions(+), 34 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index fa515b7ae76..a63181e72f1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -132,6 +132,8 @@ executors: macos: xcode: *xcode_version resource_class: macos.x86.medium.gen2 + environment: + - BUILD_FROM_SOURCE: true # ------------------------- # COMMANDS @@ -1142,6 +1144,7 @@ jobs: environment: - HERMES_WS_DIR: *hermes_workspace_root - HERMES_VERSION_FILE: "sdks/.hermesversion" + - BUILD_FROM_SOURCE: true steps: - run: name: Install dependencies diff --git a/sdks/hermes-engine/hermes-engine.podspec b/sdks/hermes-engine/hermes-engine.podspec index 0018605af03..00dfcc4d90a 100644 --- a/sdks/hermes-engine/hermes-engine.podspec +++ b/sdks/hermes-engine/hermes-engine.podspec @@ -17,42 +17,13 @@ version = package['version'] # sdks/.hermesversion hermestag_file = File.join(react_native_path, "sdks", ".hermesversion") -isInCI = ENV['CI'] === 'true' +build_from_source = ENV['BUILD_FROM_SOURCE'] === 'true' -source = {} git = "https://github.com/facebook/hermes.git" -isInMain = version.include?('1000.0.0') -isNightly = version.start_with?('0.0.0-') +abort_if_invalid_tarball_provided! -if ENV.has_key?('HERMES_ENGINE_TARBALL_PATH') - if !File.exist?(ENV['HERMES_ENGINE_TARBALL_PATH']) - abort "[Hermes] HERMES_ENGINE_TARBALL_PATH is set, but points to a non-existing file: \"#{ENV['HERMES_ENGINE_TARBALL_PATH']}\"\nIf you don't want to use tarball, run `unset HERMES_ENGINE_TARBALL_PATH`" - end -end - -if ENV.has_key?('HERMES_ENGINE_TARBALL_PATH') - Pod::UI.puts "[Hermes] Using pre-built Hermes binaries from local path: #{ENV['HERMES_ENGINE_TARBALL_PATH']}".yellow if Object.const_defined?("Pod::UI") - source[:http] = "file://#{ENV['HERMES_ENGINE_TARBALL_PATH']}" -elsif isInMain - Pod::UI.puts '[Hermes] Installing hermes-engine may take slightly longer, building Hermes compiler from source...'.yellow if Object.const_defined?("Pod::UI") - source[:git] = git - source[:commit] = `git ls-remote https://github.com/facebook/hermes main | cut -f 1`.strip -elsif isNightly - Pod::UI.puts '[Hermes] Nightly version, download pre-built for Hermes'.yellow if Object.const_defined?("Pod::UI") - destination_path = download_nightly_hermes(react_native_path, version) - # set tarball as hermes engine - source[:http] = "file://#{destination_path}" -elsif File.exists?(hermestag_file) && isInCI - Pod::UI.puts '[Hermes] Detected that you are on a React Native release branch, building Hermes from source but fetched from tag...'.yellow if Object.const_defined?("Pod::UI") - hermestag = File.read(hermestag_file).strip - source[:git] = git - source[:tag] = hermestag -else - # Sample url from Maven: - # https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.71.0/react-native-artifacts-0.71.0-hermes-ios-debug.tar.gz - source[:http] = "https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/#{version}/react-native-artifacts-#{version}-hermes-ios-#{build_type.to_s}.tar.gz" -end +source = compute_hermes_source(build_from_source, hermestag_file, git, version, build_type, react_native_path) Pod::Spec.new do |spec| spec.name = "hermes-engine" diff --git a/sdks/hermes-engine/hermes-utils.rb b/sdks/hermes-engine/hermes-utils.rb index 08424960110..e6290a57f6c 100644 --- a/sdks/hermes-engine/hermes-utils.rb +++ b/sdks/hermes-engine/hermes-utils.rb @@ -6,6 +6,98 @@ require 'net/http' require 'rexml/document' +# This function abort the build if the `HERMES_ENGINE_TARBALL_PATH` ENV variable is set with an invalid path +def abort_if_invalid_tarball_provided!() + if ENV.has_key?('HERMES_ENGINE_TARBALL_PATH') && !File.exist?(ENV['HERMES_ENGINE_TARBALL_PATH']) + abort "[Hermes] HERMES_ENGINE_TARBALL_PATH is set, but points to a non-existing file: \"#{ENV['HERMES_ENGINE_TARBALL_PATH']}\"\nIf you don't want to use tarball, run `unset HERMES_ENGINE_TARBALL_PATH`" + end +end + +# It computes the right value for the hermes-engine.podspec's source. +# - To use a specific tarball, install the dependencies with: +# `HERMES_ENGINE_TARBALL_PATH= bundle exec pod install` +# - To force a build from source, install the dependencies with: +# `BUILD_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 building from source (the latest commit on main). +# +# Parameters: +# - build_from_source: boolean to force a build from source. +# - hermestag_file: path to the hermes tag file. +# - git: uri to the hermes repository +# - version: current version of the pod +# - build_type: build type of the hermes engine. It can be `:release` or `:debug` +# - react_native_path: path to react native +# +# Returns: a properly configured source object +def compute_hermes_source(build_from_source, hermestag_file, git, version, build_type, react_native_path) + source = {} + + if ENV.has_key?('HERMES_ENGINE_TARBALL_PATH') + use_tarball(source) + elsif build_from_source + if File.exists?(hermestag_file) + build_from_tagfile(source, git, hermestag_file) + else + build_hermes_from_source(source, git) + end + elsif hermes_artifact_exists(release_tarball_url(version, build_type)) + use_release_tarball(source, version, build_type) + elsif hermes_artifact_exists(nightly_tarball_url(version).gsub("\\", "")) + use_nightly_tarball(source, react_native_path, version) + else + build_hermes_from_source(source, git) + end + + return source +end + +def use_tarball(source) + tarball_path = ENV['HERMES_ENGINE_TARBALL_PATH'] + putsIfPodPresent("[Hermes] Using pre-built Hermes binaries from local path: #{tarball_path}") + source[:http] = "file://#{tarball_path}" +end + +def build_from_tagfile(source, git, hermestag_file) + hermestag = File.read(hermestag_file).strip + putsIfPodPresent("[Hermes] Building Hermes from source from tag #{hermestag}...") + source[:git] = git + source[:tag] = hermestag +end + +def use_release_tarball(source, version, build_type) + # Sample url from Maven: + # https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.71.0/react-native-artifacts-0.71.0-hermes-ios-debug.tar.gz + putsIfPodPresent('[Hermes] Using the release tarball from Maven Central', 'info') + source[:http] = release_tarball_url(version, build_type) +end + +def release_tarball_url(version, build_type) + return "https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/#{version}/react-native-artifacts-#{version}-hermes-ios-#{build_type.to_s}.tar.gz" +end + +def use_nightly_tarball(source, react_native_path, version) + putsIfPodPresent('[Hermes] Nightly version, download pre-built for Hermes') + destination_path = download_nightly_hermes(react_native_path, version) + # set tarball as hermes engine + source[:http] = "file://#{destination_path}" +end + +def putsIfPodPresent(message, level = 'warning') + unless Object.const_defined?("Pod::UI") + return + end + + case level + when 'info' + Pod::UI.puts message.green + when 'error' + Pod::UI.puts message.red + else + Pod::UI.puts message.yellow + end +end + # This function downloads the nightly prebuilt version of Hermes based on the passed version # and save it in the node_module/react_native/sdks/downloads folder # It then returns the path to the hermes tarball @@ -15,8 +107,7 @@ require 'rexml/document' # - version: the version of React Native that requires the Hermes tarball # Returns: the path to the downloaded Hermes tarball def download_nightly_hermes(react_native_path, version) - params = "r=snapshots\&g=com.facebook.react\&a=react-native-artifacts\&c=hermes-ios-debug\&e=tar.gz\&v=#{version}-SNAPSHOT" - tarball_url = "http://oss.sonatype.org/service/local/artifact/maven/redirect\?#{params}" + tarball_url = nightly_tarball_url(version) destination_folder = "#{react_native_path}/sdks/downloads" destination_path = "#{destination_folder}/hermes-ios.tar.gz" @@ -24,3 +115,26 @@ def download_nightly_hermes(react_native_path, version) `mkdir -p "#{destination_folder}" && curl "#{tarball_url}" -Lo "#{destination_path}"` return destination_path end + +def nightly_tarball_url(version) + params = "r=snapshots\&g=com.facebook.react\&a=react-native-artifacts\&c=hermes-ios-debug\&e=tar.gz\&v=#{version}-SNAPSHOT" + return "http://oss.sonatype.org/service/local/artifact/maven/redirect\?#{params}" +end + +def build_hermes_from_source(source, git) + putsIfPodPresent('[Hermes] Installing hermes-engine may take slightly longer, building Hermes compiler from source...') + source[:git] = git + source[:commit] = `git ls-remote https://github.com/facebook/hermes main | cut -f 1`.strip +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