From ea6706e1662003dafede923e56bcb16e0acb8d35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ramos?= Date: Tue, 24 May 2022 14:08:48 -0700 Subject: [PATCH] Hermes: Add scripts to package, remove shelljs Summary: The new Hermes scripts need to be included in the `react-native` npm. The `shelljs` dependency that was used by the Hermes scripts is a dev dependency, so instead of adding to the `react-native` npm size, we refactored its use out of hermes-utils.js. Changelog: [General][Added] - Add Hermes scripts to package Reviewed By: cortinico Differential Revision: D36387135 fbshipit-source-id: 12d0bc29d365c4cb18d33a0d390e6e7d34864b7a --- package.json | 5 + scripts/__tests__/hermes-utils-test.js | 254 +++++++++++++++++++++++++ scripts/hermes/hermes-utils.js | 78 +++++--- 3 files changed, 306 insertions(+), 31 deletions(-) create mode 100644 scripts/__tests__/hermes-utils-test.js diff --git a/package.json b/package.json index fd94629cd44..752d8e4bdf6 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,11 @@ "scripts/generate-artifacts.js", "scripts/generate-provider-cli.js", "scripts/generate-specs-cli.js", + "scripts/codegen/codegen-utils.js", + "scripts/codegen/generate-artifacts-executor.js", + "scripts/codegen/generate-specs-cli-executor.js", + "scripts/hermes/hermes-utils.js", + "scripts/hermes/prepare-hermes-for-build.js", "scripts/ios-configure-glog.sh", "scripts/xcode/with-environment.sh", "scripts/launchPackager.bat", diff --git a/scripts/__tests__/hermes-utils-test.js b/scripts/__tests__/hermes-utils-test.js new file mode 100644 index 00000000000..279a4bd5cd0 --- /dev/null +++ b/scripts/__tests__/hermes-utils-test.js @@ -0,0 +1,254 @@ +/** + * 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. + * + * @format + */ + +import * as path from 'path'; + +const { + copyBuildScripts, + downloadHermesTarball, + expandHermesTarball, + getHermesTagSHA, + readHermesTag, + setHermesTag, +} = require('../hermes/hermes-utils'); + +const hermesTag = + 'hermes-2022-04-28-RNv0.69.0-15d07c2edd29a4ea0b8f15ab0588a0c1adb1200f'; +const tarballContents = 'dummy string'; +const hermesTagSha = '5244f819b2f3949ca94a3a1bf75d54a8ed59d94a'; + +const ROOT_DIR = path.normalize(path.join(__dirname, '..', '..')); +const SDKS_DIR = path.join(ROOT_DIR, 'sdks'); + +const MemoryFs = require('metro-memory-fs'); + +let execCalls; +let fs; + +jest.mock('child_process', () => ({ + execSync: jest.fn(command => { + if (command.startsWith('curl')) { + fs.writeFileSync( + path.join(SDKS_DIR, 'download', `hermes-${hermesTagSha}.tgz`), + tarballContents, + ); + execCalls.curl = true; + return {code: 0}; + } + + if (command.startsWith('git')) { + execCalls.git = true; + return hermesTagSha + '\n'; + } + + if (command.startsWith('tar')) { + fs.mkdirSync(path.join(SDKS_DIR, 'hermes', 'utils'), { + recursive: true, + }); + fs.writeFileSync(path.join(SDKS_DIR, 'hermes', `package.json`), '{}'); + execCalls.tar = true; + return {code: 0}; + } + }), +})); + +function populateMockFilesystem() { + fs.mkdirSync(path.join(SDKS_DIR, 'hermes-engine', 'utils'), { + recursive: true, + }); + fs.writeFileSync( + path.join( + ROOT_DIR, + 'sdks', + 'hermes-engine', + 'utils', + 'build-apple-framework.sh', + ), + 'Dummy file', + ); + fs.writeFileSync( + path.join( + ROOT_DIR, + 'sdks', + 'hermes-engine', + 'utils', + 'build-ios-framework.sh', + ), + 'Dummy file', + ); + fs.writeFileSync( + path.join( + ROOT_DIR, + 'sdks', + 'hermes-engine', + 'utils', + 'build-mac-framework.sh', + ), + 'Dummy file', + ); + fs.writeFileSync( + path.join(SDKS_DIR, 'hermes-engine', 'hermes-engine.podspec'), + 'Dummy file', + ); +} + +describe('hermes-utils', () => { + beforeEach(() => { + jest.resetModules(); + + jest.mock( + 'fs', + () => + new MemoryFs({ + platform: process.platform === 'win32' ? 'win32' : 'posix', + }), + ); + fs = require('fs'); + fs.reset(); + + populateMockFilesystem(); + + execCalls = Object.create(null); + }); + describe('readHermesTag', () => { + it('should return main if .hermesversion does not exist', () => { + expect(readHermesTag()).toEqual('main'); + }); + it('should fail if hermes tag is empty', () => { + fs.writeFileSync(path.join(SDKS_DIR, '.hermesversion'), ''); + expect(() => { + readHermesTag(); + }).toThrow('[Hermes] .hermesversion file is empty.'); + }); + it('should return tag from .hermesversion if file exists', () => { + fs.writeFileSync(path.join(SDKS_DIR, '.hermesversion'), hermesTag); + expect(readHermesTag()).toEqual(hermesTag); + }); + }); + describe('setHermesTag', () => { + it('should write tag to .hermesversion file', () => { + setHermesTag(hermesTag); + expect( + fs.readFileSync(path.join(SDKS_DIR, '.hermesversion'), { + encoding: 'utf8', + flag: 'r', + }), + ).toEqual(hermesTag); + }); + it('should set Hermes tag and read it back', () => { + setHermesTag(hermesTag); + expect(readHermesTag()).toEqual(hermesTag); + }); + }); + describe('getHermesTagSHA', () => { + it('should return trimmed commit SHA for Hermes tag', () => { + expect(getHermesTagSHA(hermesTag)).toEqual(hermesTagSha); + expect(execCalls.git).toBeTruthy(); + }); + }); + describe('downloadHermesTarball', () => { + it('should download Hermes tarball to download dir', () => { + fs.writeFileSync(path.join(SDKS_DIR, '.hermesversion'), hermesTag); + downloadHermesTarball(); + expect(execCalls.curl).toBeTruthy(); + expect( + fs.readFileSync( + path.join(SDKS_DIR, 'download', `hermes-${hermesTagSha}.tgz`), + { + encoding: 'utf8', + flag: 'r', + }, + ), + ).toEqual(tarballContents); + }); + it('should not re-download Hermes tarball if tarball exists', () => { + fs.mkdirSync(path.join(SDKS_DIR, 'download'), {recursive: true}); + fs.writeFileSync( + path.join(SDKS_DIR, 'download', `hermes-${hermesTagSha}.tgz`), + tarballContents, + ); + + downloadHermesTarball(); + expect(execCalls.curl).toBeUndefined(); + }); + }); + describe('expandHermesTarball', () => { + it('should expand Hermes tarball to Hermes source dir', () => { + fs.mkdirSync(path.join(SDKS_DIR, 'download'), {recursive: true}); + fs.writeFileSync( + path.join(SDKS_DIR, 'download', `hermes-${hermesTagSha}.tgz`), + tarballContents, + ); + expect(fs.existsSync(path.join(SDKS_DIR, 'hermes'))).toBeFalsy(); + expandHermesTarball(); + expect(execCalls.tar).toBe(true); + expect(fs.existsSync(path.join(SDKS_DIR, 'hermes'))).toBeTruthy(); + }); + it('should fail if Hermes tarball does not exist', () => { + expect(() => { + expandHermesTarball(); + }).toThrow('[Hermes] Failed to expand 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', + ), + { + encoding: 'utf8', + flag: 'r', + }, + ), + ).toEqual( + fs.readFileSync( + path.join( + ROOT_DIR, + 'sdks', + 'hermes-engine', + 'utils', + 'build-mac-framework.sh', + ), + { + encoding: 'utf8', + flag: 'r', + }, + ), + ); + expect( + fs.readFileSync( + path.join(SDKS_DIR, 'hermes', 'hermes-engine.podspec'), + { + encoding: 'utf8', + flag: 'r', + }, + ), + ).toEqual( + fs.readFileSync( + path.join(SDKS_DIR, 'hermes-engine', 'hermes-engine.podspec'), + { + encoding: 'utf8', + flag: 'r', + }, + ), + ); + }); + }); +}); diff --git a/scripts/hermes/hermes-utils.js b/scripts/hermes/hermes-utils.js index 84fd59d520f..53098a03f77 100644 --- a/scripts/hermes/hermes-utils.js +++ b/scripts/hermes/hermes-utils.js @@ -11,7 +11,7 @@ const fs = require('fs'); const path = require('path'); -const {echo, exec, exit} = require('shelljs'); +const {execSync} = require('child_process'); const SDKS_DIR = path.normalize(path.join(__dirname, '..', '..', 'sdks')); const HERMES_DIR = path.join(SDKS_DIR, 'hermes'); @@ -41,14 +41,21 @@ function prepareFileSystem() { function readHermesTag() { if (fs.existsSync(HERMES_TAG_FILE_PATH)) { - const data = fs.readFileSync(HERMES_TAG_FILE_PATH, { - encoding: 'utf8', - flag: 'r', - }); - return data.trim(); - } else { - return 'main'; + const data = fs + .readFileSync(HERMES_TAG_FILE_PATH, { + encoding: 'utf8', + flag: 'r', + }) + .trim(); + + if (data.length > 0) { + return data; + } else { + throw new Error('[Hermes] .hermesversion file is empty.'); + } } + + return 'main'; } function setHermesTag(hermesTag) { @@ -64,10 +71,11 @@ function setHermesTag(hermesTag) { } function getHermesTagSHA(hermesTag) { - return exec( + return execSync( `git ls-remote https://github.com/facebook/hermes ${hermesTag} | cut -f 1`, - {silent: true}, - ).trim(); + ) + .toString() + .trim(); } function getHermesTarballDownloadPath(hermesTag) { @@ -87,11 +95,13 @@ function downloadHermesTarball() { return; } - echo(`[Hermes] Downloading Hermes source code for commit ${hermesTagSHA}`); - if (exec(`curl ${hermesTarballUrl} -Lo ${hermesTarballDownloadPath}`).code) { - echo('[Hermes] Failed to download Hermes tarball.'); - exit(1); - return; + console.info( + `[Hermes] Downloading Hermes source code for commit ${hermesTagSHA}`, + ); + try { + execSync(`curl ${hermesTarballUrl} -Lo ${hermesTarballDownloadPath}`); + } catch (error) { + throw new Error(`[Hermes] Failed to download Hermes tarball. ${error}`); } } @@ -103,26 +113,26 @@ function expandHermesTarball() { prepareFileSystem(); if (!fs.existsSync(hermesTarballDownloadPath)) { - echo( - `[Hermes] Failed to expand Hermes tarball, no file found at ${hermesTarballDownloadPath}.`, - ); - exit(1); - return; + throw new Error(`[Hermes] Failed to expand Hermes tarball.`); } - echo(`[Hermes] Expanding Hermes tarball for commit ${hermesTagSHA}`); - if ( - exec( + console.info(`[Hermes] Expanding Hermes tarball for commit ${hermesTagSHA}`); + try { + execSync( `tar -zxf ${hermesTarballDownloadPath} --strip-components=1 --directory ${HERMES_DIR}`, - ).code - ) { - echo('[Hermes] Failed to expand Hermes tarball.'); - exit(1); - return; + ); + } catch (error) { + throw new Error('[Hermes] Failed to expand Hermes tarball.'); } } function copyBuildScripts() { + if (!fs.existsSync(HERMES_DIR)) { + throw new Error( + '[Hermes] Failed to copy Hermes build scripts, no Hermes source directory found.', + ); + } + fs.copyFileSync( `${SDKS_DIR}/hermes-engine/hermes-engine.podspec`, `${HERMES_DIR}/hermes-engine.podspec`, @@ -155,8 +165,14 @@ set_target_properties(native-hermesc PROPERTIES IMPORTED_LOCATION "${MACOS_HERMESC_PATH}" )`; - fs.mkdirSync(MACOS_BIN_DIR, {recursive: true}); - fs.writeFileSync(MACOS_IMPORT_HERMESC_PATH, IMPORT_HERMESC_TEMPLATE); + try { + fs.mkdirSync(MACOS_BIN_DIR, {recursive: true}); + fs.writeFileSync(MACOS_IMPORT_HERMESC_PATH, IMPORT_HERMESC_TEMPLATE); + } catch (error) { + console.warn( + `[Hermes] Re-compiling hermesc. Unable to configure make: ${error}`, + ); + } } module.exports = {