Files
react-native/scripts/__tests__/hermes-utils-test.js
T
Héctor Ramos 31bedd9815 Add hermes/create-tarball.js script (#34875)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/34875

Create common script for generating a Hermes tarball after Hermes is built from source.

Use after building Hermes from source to create a tarball of the resulting build artifacts. The path to the tarball can be passed to CocoaPods via a `HERMES_ENGINE_TARBALL_PATH` envvar in order to use these pre-built Hermes artifacts when installing the `hermes-engine` pod with `pod install`.

Use in Circle CI when creating a Hermes tarball for caching and for use in stable React Native releases.

Usage:

```
pod install

# When Hermes is built from source via CocoaPods, the build artifacts will be located in the Pods directory for hermes-engine

node ./scripts/hermes/create-tarball.js \
  --inputDir ./sdks/hermes \
  --buildType Debug \
  --releaseVersion 1000.0.0 \
  --outputDir .
```

Changelog: [Internal]

Reviewed By: cipolleschi

Differential Revision: D40124378

fbshipit-source-id: f9712e87526ccc737afac4599b0ab0a7bb3f3956
2022-10-07 10:21:25 -07:00

316 lines
8.8 KiB
JavaScript

/**
* 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 {
configureMakeForPrebuiltHermesC,
copyBuildScripts,
copyPodSpec,
downloadHermesTarball,
expandHermesTarball,
getHermesTarballName,
getHermesTagSHA,
readHermesTag,
setHermesTag,
shouldUsePrebuiltHermesC,
} = require('../hermes/hermes-utils');
const hermesTag =
'hermes-2022-04-28-RNv0.69.0-15d07c2edd29a4ea0b8f15ab0588a0c1adb1200f';
const tarballContents = 'dummy string';
const hermescContents = '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('getHermesTarballName', () => {
it('should return Hermes tarball name', () => {
expect(getHermesTarballName('Debug', '1000.0.0')).toEqual(
'hermes-runtime-darwin-debug-v1000.0.0.tar.gz',
);
});
it('should throw if build type is undefined', () => {
expect(() => {
getHermesTarballName();
}).toThrow('Did not specify build type.');
});
it('should throw if release version is undefined', () => {
expect(() => {
getHermesTarballName('Release');
}).toThrow('Did not specify release version.');
});
it('should return debug Hermes tarball name for RN 0.70.0', () => {
expect(getHermesTarballName('Debug', '0.70.0')).toEqual(
'hermes-runtime-darwin-debug-v0.70.0.tar.gz',
);
});
it('should return a wildcard Hermes tarball name for any RN version', () => {
expect(getHermesTarballName('Debug', '*')).toEqual(
'hermes-runtime-darwin-debug-v*.tar.gz',
);
});
});
describe('getHermesTagSHA', () => {
it('should return trimmed commit SHA for Hermes tag', () => {
expect(getHermesTagSHA(hermesTag)).toEqual(hermesTagSha);
expect(execCalls.git).toBe(true);
});
});
describe('downloadHermesTarball', () => {
it('should download Hermes tarball to download dir', () => {
fs.writeFileSync(path.join(SDKS_DIR, '.hermesversion'), hermesTag);
downloadHermesTarball();
expect(execCalls.curl).toBe(true);
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'))).toBe(true);
});
it('should fail if Hermes tarball does not exist', () => {
expect(() => {
expandHermesTarball();
}).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', () => {
copyBuildScripts();
[
'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',
},
),
).toEqual(
fs.readFileSync(
path.join(ROOT_DIR, 'sdks', 'hermes-engine', 'utils', buildScript),
{
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'),
{
encoding: 'utf8',
flag: 'r',
},
),
).toEqual(
fs.readFileSync(
path.join(SDKS_DIR, 'hermes-engine', 'hermes-engine.podspec'),
{
encoding: 'utf8',
flag: 'r',
},
),
);
});
});
describe('shouldUsePrebuiltHermesC', () => {
it('returns false if path to osx hermesc does not exist', () => {
expect(shouldUsePrebuiltHermesC('macos')).toBeFalsy();
});
it('returns false for non-macOS', () => {
expect(shouldUsePrebuiltHermesC('windows')).toBeFalsy();
});
it('return true only if path to hermesc exists', () => {
fs.mkdirSync(path.join(SDKS_DIR, 'hermesc', 'osx-bin'), {
recursive: true,
});
fs.writeFileSync(
path.join(SDKS_DIR, 'hermesc', 'osx-bin', 'hermesc'),
hermescContents,
);
expect(shouldUsePrebuiltHermesC('macos')).toBe(true);
});
});
describe('configureMakeForPrebuiltHermesC', () => {
it('creates ImportHermesC file', () => {
fs.mkdirSync(path.join(SDKS_DIR, 'hermesc', 'osx-bin'), {
recursive: true,
});
configureMakeForPrebuiltHermesC();
expect(
fs.existsSync(
path.join(SDKS_DIR, 'hermesc', 'osx-bin', 'ImportHermesc.cmake'),
),
).toBe(true);
});
});
});