Skip to content

Commit

Permalink
Hermes: Use pre-built artifacts in hermes-engine
Browse files Browse the repository at this point in the history
Summary:
Update `hermes-engine.podspec` to use pre-built Hermes artifacts from the corresponding React Native GitHub Release when targeting a specific React Native release.
Otherwise, fallback to building Hermes from source.

Changelog: [Internal]

Reviewed By: cortinico, cipolleschi

Differential Revision: D36609257

fbshipit-source-id: 6179c9e255558c7eaf1417ff46a2e7db120295f0
  • Loading branch information
hramos authored and facebook-github-bot committed May 25, 2022
1 parent a709688 commit 38b7065
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 102 deletions.
60 changes: 29 additions & 31 deletions scripts/__tests__/hermes-utils-test.js
Expand Up @@ -11,6 +11,7 @@ import * as path from 'path';

const {
copyBuildScripts,
copyPodSpec,
downloadHermesTarball,
expandHermesTarball,
getHermesTagSHA,
Expand Down Expand Up @@ -193,45 +194,42 @@ describe('hermes-utils', () => {
it('should fail if Hermes tarball does not exist', () => {
expect(() => {
expandHermesTarball();
}).toThrow('[Hermes] Failed to expand Hermes tarball.');
}).toThrow('[Hermes] Could not locate Hermes tarball.');
expect(execCalls.tar).toBeUndefined();
});
});
describe('copyBuildScripts', () => {
it('should copy React Native Hermes build scripts to Hermes source directory', () => {
fs.mkdirSync(path.join(SDKS_DIR, 'hermes', 'utils'), {
recursive: true,
});
copyBuildScripts();
expect(
fs.readFileSync(
path.join(
ROOT_DIR,
'sdks',
'hermes',
'utils',
'build-mac-framework.sh',

[
'build-apple-framework.sh',
'build-ios-framework.sh',
'build-mac-framework.sh',
].forEach(buildScript => {
expect(
fs.readFileSync(
path.join(ROOT_DIR, 'sdks', 'hermes', 'utils', buildScript),
{
encoding: 'utf8',
flag: 'r',
},
),
{
encoding: 'utf8',
flag: 'r',
},
),
).toEqual(
fs.readFileSync(
path.join(
ROOT_DIR,
'sdks',
'hermes-engine',
'utils',
'build-mac-framework.sh',
).toEqual(
fs.readFileSync(
path.join(ROOT_DIR, 'sdks', 'hermes-engine', 'utils', buildScript),
{
encoding: 'utf8',
flag: 'r',
},
),
{
encoding: 'utf8',
flag: 'r',
},
),
);
);
});
});
});
describe('copyPodSpec', () => {
it('should copy React Native Hermes Podspec to Hermes source directory', () => {
copyPodSpec();
expect(
fs.readFileSync(
path.join(SDKS_DIR, 'hermes', 'hermes-engine.podspec'),
Expand Down
68 changes: 41 additions & 27 deletions scripts/hermes/hermes-utils.js
Expand Up @@ -15,6 +15,7 @@ const {execSync} = require('child_process');

const SDKS_DIR = path.normalize(path.join(__dirname, '..', '..', 'sdks'));
const HERMES_DIR = path.join(SDKS_DIR, 'hermes');
const DEFAULT_HERMES_TAG = 'main';
const HERMES_TAG_FILE_PATH = path.join(SDKS_DIR, '.hermesversion');
const HERMES_TARBALL_BASE_URL = 'https://github.com/facebook/hermes/tarball/';
const HERMES_TARBALL_DOWNLOAD_DIR = path.join(SDKS_DIR, 'download');
Expand All @@ -25,20 +26,6 @@ const MACOS_IMPORT_HERMESC_PATH = path.join(
'ImportHermesc.cmake',
);

function prepareFileSystem() {
if (!fs.existsSync(SDKS_DIR)) {
fs.mkdirSync(SDKS_DIR, {recursive: true});
}

if (!fs.existsSync(HERMES_DIR)) {
fs.mkdirSync(HERMES_DIR, {recursive: true});
}

if (!fs.existsSync(HERMES_TARBALL_DOWNLOAD_DIR)) {
fs.mkdirSync(HERMES_TARBALL_DOWNLOAD_DIR, {recursive: true});
}
}

function readHermesTag() {
if (fs.existsSync(HERMES_TAG_FILE_PATH)) {
const data = fs
Expand All @@ -64,8 +51,9 @@ function setHermesTag(hermesTag) {
return;
}

prepareFileSystem();

if (!fs.existsSync(SDKS_DIR)) {
fs.mkdirSync(SDKS_DIR, {recursive: true});
}
fs.writeFileSync(HERMES_TAG_FILE_PATH, hermesTag.trim());
console.log('Hermes tag has been updated. Please commit your changes.');
}
Expand All @@ -89,12 +77,14 @@ function downloadHermesTarball() {
const hermesTarballDownloadPath = getHermesTarballDownloadPath(hermesTag);
let hermesTarballUrl = HERMES_TARBALL_BASE_URL + hermesTag;

prepareFileSystem();

if (fs.existsSync(hermesTarballDownloadPath)) {
return;
}

if (!fs.existsSync(HERMES_TARBALL_DOWNLOAD_DIR)) {
fs.mkdirSync(HERMES_TARBALL_DOWNLOAD_DIR, {recursive: true});
}

console.info(
`[Hermes] Downloading Hermes source code for commit ${hermesTagSHA}`,
);
Expand All @@ -110,12 +100,13 @@ function expandHermesTarball() {
const hermesTagSHA = getHermesTagSHA(hermesTag);
const hermesTarballDownloadPath = getHermesTarballDownloadPath(hermesTag);

prepareFileSystem();

if (!fs.existsSync(hermesTarballDownloadPath)) {
throw new Error(`[Hermes] Failed to expand Hermes tarball.`);
throw new Error('[Hermes] Could not locate Hermes tarball.');
}

if (!fs.existsSync(HERMES_DIR)) {
fs.mkdirSync(HERMES_DIR, {recursive: true});
}
console.info(`[Hermes] Expanding Hermes tarball for commit ${hermesTagSHA}`);
try {
execSync(
Expand All @@ -127,16 +118,16 @@ function expandHermesTarball() {
}

function copyBuildScripts() {
if (!fs.existsSync(HERMES_DIR)) {
if (!fs.existsSync(SDKS_DIR)) {
throw new Error(
'[Hermes] Failed to copy Hermes build scripts, no Hermes source directory found.',
'[Hermes] Failed to copy Hermes build scripts, no SDKs directory found.',
);
}

fs.copyFileSync(
path.join(SDKS_DIR, 'hermes-engine', 'hermes-engine.podspec'),
path.join(HERMES_DIR, 'hermes-engine.podspec'),
);
if (!fs.existsSync(HERMES_DIR)) {
fs.mkdirSync(path.join(HERMES_DIR, 'utils'), {recursive: true});
}

fs.copyFileSync(
path.join(SDKS_DIR, 'hermes-engine', 'utils', 'build-apple-framework.sh'),
path.join(HERMES_DIR, 'utils', 'build-apple-framework.sh'),
Expand All @@ -151,6 +142,27 @@ function copyBuildScripts() {
);
}

function copyPodSpec() {
if (!fs.existsSync(SDKS_DIR)) {
throw new Error(
'[Hermes] Failed to copy Hermes Podspec, no SDKs directory found.',
);
}

if (!fs.existsSync(HERMES_DIR)) {
fs.mkdirSync(HERMES_DIR, {recursive: true});
}
fs.copyFileSync(
path.join(SDKS_DIR, 'hermes-engine', 'hermes-engine.podspec'),
path.join(HERMES_DIR, 'hermes-engine.podspec'),
);
}

function shouldBuildHermesFromSource() {
const hermesTag = readHermesTag();
return hermesTag === DEFAULT_HERMES_TAG;
}

function shouldUsePrebuiltHermesC(os) {
if (os === 'macos') {
return fs.existsSync(MACOS_HERMESC_PATH);
Expand Down Expand Up @@ -178,10 +190,12 @@ set_target_properties(native-hermesc PROPERTIES
module.exports = {
configureMakeForPrebuiltHermesC,
copyBuildScripts,
copyPodSpec,
downloadHermesTarball,
expandHermesTarball,
getHermesTagSHA,
readHermesTag,
setHermesTag,
shouldBuildHermesFromSource,
shouldUsePrebuiltHermesC,
};
26 changes: 20 additions & 6 deletions scripts/hermes/prepare-hermes-for-build.js
Expand Up @@ -16,16 +16,30 @@
const {
configureMakeForPrebuiltHermesC,
copyBuildScripts,
copyPodSpec,
downloadHermesTarball,
expandHermesTarball,
shouldUsePrebuiltHermesC,
shouldBuildHermesFromSource,
} = require('./hermes-utils');

downloadHermesTarball();
expandHermesTarball();
copyBuildScripts();
async function main() {
if (!shouldBuildHermesFromSource()) {
copyPodSpec();
return;
}

if (shouldUsePrebuiltHermesC('macos')) {
console.log('[Hermes] Using pre-built HermesC');
configureMakeForPrebuiltHermesC();
downloadHermesTarball();
expandHermesTarball();
copyPodSpec();
copyBuildScripts();

if (shouldUsePrebuiltHermesC('macos')) {
console.log('[Hermes] Using pre-built HermesC');
configureMakeForPrebuiltHermesC();
}
}

main().then(() => {
process.exit(0);
});
21 changes: 2 additions & 19 deletions scripts/react_native_pods.rb
Expand Up @@ -107,25 +107,8 @@ def use_react_native! (options={})
if hermes_enabled
pod 'React-hermes', :path => "#{prefix}/ReactCommon/hermes"
pod 'libevent', '~> 2.1.12'

sdks_dir = Pod::Config.instance.installation_root.join(prefix, "sdks")
hermes_tag_file = sdks_dir.join(".hermesversion")

if (File.exist?(hermes_tag_file))
# Use published pod with pre-builts.
Pod::UI.puts "[Hermes] Tag file exists at path: #{hermes_tag_file}"
package = JSON.parse(File.read(File.join(__dir__, "..", "package.json")))
hermes_version = package['version']
Pod::UI.puts "[Hermes] Loading version: #{hermes_version}"
pod 'hermes-engine', hermes_version
else
# Use local podspec and build from source.
path_to_hermes = "#{prefix}/sdks/hermes/hermes-engine.podspec"
Pod::UI.puts "[Hermes] Use local version from #{path_to_hermes}"
system("(cd #{prefix} && node scripts/hermes/prepare-hermes-for-build)")
pod 'hermes-engine', :path => path_to_hermes
end

system("(cd #{prefix} && node scripts/hermes/prepare-hermes-for-build)")
pod 'hermes-engine', :path => "#{prefix}/sdks/hermes/hermes-engine.podspec"
end

if flipper_configuration.flipper_enabled && !production
Expand Down
25 changes: 6 additions & 19 deletions sdks/hermes-engine/hermes-engine.podspec
Expand Up @@ -5,9 +5,6 @@

require "json"

# sdks/.hermesversion
hermes_tag_file = File.join(__dir__, "..", ".hermesversion")

# sdks/hermesc/osx-bin/ImportHermesc.cmake
import_hermesc_file=File.join(__dir__, "..", "hermesc", "osx-bin", "ImportHermesc.cmake")

Expand All @@ -18,37 +15,27 @@ version = package['version']

source = {}
git = "https://github.com/facebook/hermes.git"
building_from_source = false

if ENV['hermes-artifact-url'] then
source[:http] = ENV['hermes-artifact-url']
elsif File.exist?(hermes_tag_file) then
building_from_source = true
if version == '1000.0.0'
Pod::UI.puts '[Hermes] Hermes needs to be compiled, installing hermes-engine may take a while...'.yellow if Object.const_defined?("Pod::UI")
source[:git] = git
source[:tag] = File.read(hermes_tag_file)
source[:commit] = `git ls-remote https://github.com/facebook/hermes main | cut -f 1`.strip
else
building_from_source = true
source[:git] = git
hermes_commit_sha = `git ls-remote https://github.com/facebook/hermes main | cut -f 1`.strip
source[:commit] = hermes_commit_sha
source[:http] = `https://github.com/facebook/react-native/releases/download/v#{version}/hermes-runtime-darwin-v#{version}.tar.gz`
end

module HermesHelper
# BUILD_TYPE = :debug
BUILD_TYPE = :release
end

if building_from_source
Pod::UI.puts '[Hermes] Hermes needs to be compiled, installing hermes-engine may take a while...'.yellow if Object.const_defined?("Pod::UI")
end

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 = { type: "MIT", file: "LICENSE" }
spec.license = package["license"]
spec.author = "Facebook"
spec.source = source
spec.platforms = { :osx => "10.13", :ios => "11.0" }
Expand All @@ -62,7 +49,7 @@ Pod::Spec.new do |spec|

spec.xcconfig = { "CLANG_CXX_LANGUAGE_STANDARD" => "c++17", "CLANG_CXX_LIBRARY" => "compiler-default", "GCC_PREPROCESSOR_DEFINITIONS" => "HERMES_ENABLE_DEBUGGER=1" }

unless ENV['hermes-artifact-url']
if source[:git] then
spec.prepare_command = <<-EOS
# When true, debug build will be used.
# See `build-apple-framework.sh` for details
Expand Down

0 comments on commit 38b7065

Please sign in to comment.