1import spawnAsync from '@expo/spawn-async';
2import fs from 'fs-extra';
3import path from 'path';
4
5import { runReactNativeCodegenAsync } from '../../Codegen';
6import { REACT_NATIVE_SUBMODULE_DIR, REACT_NATIVE_SUBMODULE_MONOREPO_ROOT } from '../../Constants';
7import { copyFileWithTransformsAsync, transformFileAsync } from '../../Transforms';
8import { searchFilesAsync } from '../../Utils';
9import {
10  codegenTransforms,
11  hermesTransforms,
12  reactNativeTransforms,
13} from './reactNativeTransforms';
14
15export async function updateVersionedReactNativeAsync(
16  androidDir: string,
17  sdkVersion: string
18): Promise<void> {
19  const abiVersion = `abi${sdkVersion.replace(/\./g, '_')}`;
20  const versionedReactNativeMonorepoRoot = path.join(androidDir, 'versioned-react-native');
21  const versionedReactNativeRoot = path.join(
22    versionedReactNativeMonorepoRoot,
23    'packages/react-native'
24  );
25  await Promise.all([fs.remove(path.join(versionedReactNativeMonorepoRoot, 'packages'))]);
26
27  await fs.mkdirp(path.join(versionedReactNativeRoot, 'sdks'));
28  await fs.copy(
29    path.join(REACT_NATIVE_SUBMODULE_DIR, 'sdks/.hermesversion'),
30    path.join(versionedReactNativeRoot, 'sdks/.hermesversion')
31  );
32
33  // Run and version codegen
34  const codegenOutputRoot = path.join(versionedReactNativeRoot, 'codegen');
35  const tmpCodegenOutputRoot = path.join(versionedReactNativeMonorepoRoot, 'codegen-tmp');
36  try {
37    await runReactNativeCodegenAsync({
38      reactNativeRoot: REACT_NATIVE_SUBMODULE_DIR,
39      codegenPkgRoot: path.join(
40        REACT_NATIVE_SUBMODULE_MONOREPO_ROOT,
41        'packages',
42        'react-native-codegen'
43      ),
44      outputDir: tmpCodegenOutputRoot,
45      name: 'rncore',
46      platform: 'android',
47      type: 'all',
48      jsSrcsDir: path.join(REACT_NATIVE_SUBMODULE_DIR, 'Libraries'),
49      javaPackageName: 'com.facebook.fbreact.specs',
50    });
51    await versionCodegenDirectoryAsync(tmpCodegenOutputRoot, codegenOutputRoot, abiVersion);
52  } finally {
53    await fs.remove(tmpCodegenOutputRoot);
54  }
55
56  // Copy and version ReactAndroid and ReactCommon
57  await versionReactNativeAsync(versionedReactNativeRoot, abiVersion);
58
59  await versionHermesAsync(versionedReactNativeMonorepoRoot, abiVersion);
60}
61
62async function versionHermesAsync(versionedReactNativeMonorepoRoot: string, abiVersion: string) {
63  await spawnAsync('./gradlew', [':packages:react-native:ReactAndroid:hermes-engine:unzipHermes'], {
64    shell: true,
65    cwd: versionedReactNativeMonorepoRoot,
66    stdio: 'inherit',
67  });
68  await transformFileAsync(
69    path.join(
70      versionedReactNativeMonorepoRoot,
71      'packages/react-native',
72      'sdks/hermes/API/hermes/CMakeLists.txt'
73    ),
74    hermesTransforms(abiVersion)
75  );
76}
77
78async function versionReactNativeAsync(versionedReactNativeRoot: string, abiVersion: string) {
79  const files = await searchFilesAsync(REACT_NATIVE_SUBMODULE_DIR, [
80    './ReactAndroid/**',
81    './ReactCommon/**',
82  ]);
83  for (const file of files) {
84    if ((file.match(/\/build\//) && !file.match(/src.*\/build\//)) || file.match(/\/\.cxx\//)) {
85      files.delete(file);
86    }
87  }
88
89  const transforms = reactNativeTransforms(versionedReactNativeRoot, abiVersion);
90  for (const sourceFile of files) {
91    await copyFileWithTransformsAsync({
92      sourceFile,
93      targetDirectory: versionedReactNativeRoot,
94      sourceDirectory: REACT_NATIVE_SUBMODULE_DIR,
95      transforms,
96    });
97  }
98}
99
100async function versionCodegenDirectoryAsync(
101  tmpCodegenDir: string,
102  codegenDir: string,
103  abiVersion: string
104) {
105  const files = await searchFilesAsync(tmpCodegenDir, ['**']);
106  const transforms = codegenTransforms(abiVersion);
107  for (const sourceFile of files) {
108    await copyFileWithTransformsAsync({
109      sourceFile,
110      targetDirectory: codegenDir,
111      sourceDirectory: tmpCodegenDir,
112      transforms,
113    });
114  }
115}
116