1import spawnAsync from '@expo/spawn-async'; 2import fs from 'fs-extra'; 3import path from 'path'; 4 5import { transformFileAsync } from '../../Transforms'; 6 7export async function updateVersionedReactNativeAsync( 8 reactNativeRoot: string, 9 versionedReactNativeRoot: string 10): Promise<void> { 11 // Clone whole directories 12 const copyDirs = ['ReactAndroid', 'ReactCommon']; 13 await Promise.all( 14 copyDirs.map((subdir) => fs.remove(path.join(versionedReactNativeRoot, subdir))) 15 ); 16 await Promise.all( 17 copyDirs.map((subdir) => 18 fs.copy(path.join(reactNativeRoot, subdir), path.join(versionedReactNativeRoot, subdir)) 19 ) 20 ); 21 22 // Run codegen 23 const codegenOutputRoot = path.join(versionedReactNativeRoot, 'codegen'); 24 await fs.remove(codegenOutputRoot); 25 await runReactNativeCodegenAndroidAsync(reactNativeRoot, codegenOutputRoot); 26 27 // Patch ReactAndroid/build.gradle for codegen 28 const buildGradlePath = path.join(versionedReactNativeRoot, 'ReactAndroid', 'build.gradle'); 29 await transformFileAsync(buildGradlePath, [ 30 // Update codegen folder to our customized folder 31 { 32 find: /"REACT_GENERATED_SRC_DIR=.+?",/, 33 replaceWith: `"REACT_GENERATED_SRC_DIR=${versionedReactNativeRoot}",`, 34 }, 35 // Add generated java to sourceSets 36 { 37 find: /(\bsrcDirs = \["src\/main\/java",.+)(])/, 38 replaceWith: `$1, "${codegenOutputRoot}/java"$2`, 39 }, 40 // Disable codegen plugin 41 { 42 find: /(\bid\("com\.facebook\.react\.codegen"\)$)/m, 43 replaceWith: '// $1', 44 }, 45 { 46 find: /(^react {[^]+?\n\})/m, 47 replaceWith: '/* $1 */', 48 }, 49 { 50 find: /(\bdependsOn\("generateCodegenArtifactsFromSchema"\))/, 51 replaceWith: '// $1', 52 }, 53 ]); 54} 55 56async function runReactNativeCodegenAndroidAsync( 57 reactNativeRoot: string, 58 codegenOutputRoot: string 59) { 60 await fs.ensureDir(codegenOutputRoot); 61 62 // generate schema.json from js & flow types 63 const genSchemaScript = path.join( 64 reactNativeRoot, 65 'packages', 66 'react-native-codegen', 67 'lib', 68 'cli', 69 'combine', 70 'combine-js-to-schema-cli.js' 71 ); 72 const schemaOutputPath = path.join(codegenOutputRoot, 'schema.json'); 73 const jsSourceRoot = path.join(reactNativeRoot, 'Libraries'); 74 await spawnAsync('yarn', ['node', genSchemaScript, schemaOutputPath, jsSourceRoot]); 75 76 // generate code from schema.json 77 const genCodeScript = path.join(reactNativeRoot, 'scripts', 'generate-specs-cli.js'); 78 await spawnAsync('yarn', [ 79 'node', 80 genCodeScript, 81 'android', 82 schemaOutputPath, 83 codegenOutputRoot, 84 'ReactAndroidSpec', 85 'com.facebook.fbreact.specs', 86 ]); 87} 88 89export async function renameHermesEngine(versionedReactAndroidPath: string, version: string) { 90 const abiVersion = version.replace(/\./g, '_'); 91 const abiName = `abi${abiVersion}`; 92 const prebuiltHermesMkPath = path.join( 93 versionedReactAndroidPath, 94 'src', 95 'main', 96 'jni', 97 'first-party', 98 'hermes', 99 'Android.mk' 100 ); 101 const versionedHermesLibName = `libhermes_${abiName}.so`; 102 await transformFileAsync(prebuiltHermesMkPath, [ 103 { 104 find: /^(LOCAL_SRC_FILES\s+:=\s+jni\/\$\(TARGET_ARCH_ABI\))\/libhermes.so$/gm, 105 replaceWith: `$1/${versionedHermesLibName}`, 106 }, 107 ]); 108 109 const buildGradlePath = path.join(versionedReactAndroidPath, 'build.gradle'); 110 // patch prepareHermes task to rename copied library and update soname 111 // the diff is something like that: 112 // 113 // ```diff 114 // --- android/versioned-react-native/ReactAndroid/build.gradle.orig 2021-08-14 00:40:18.000000000 +0800 115 // +++ android/versioned-react-native/ReactAndroid/build.gradle 2021-08-14 00:40:58.000000000 +0800 116 // @@ -114,7 +114,7 @@ 117 // into("$thirdPartyNdkDir/folly") 118 // } 119 // 120 // -task prepareHermes(dependsOn: createNativeDepsDirectories, type: Copy) { 121 // +task prepareHermes(dependsOn: createNativeDepsDirectories) { 122 // def hermesPackagePath = findNodeModulePath(projectDir, "hermes-engine") 123 // if (!hermesPackagePath) { 124 // throw new GradleScriptException("Could not find the hermes-engine npm package", null) 125 // @@ -126,12 +126,29 @@ 126 // } 127 // 128 // def soFiles = zipTree(hermesAAR).matching({ it.include "**/*.so" }) 129 // - 130 // + copy { 131 // + 132 // from soFiles 133 // from "src/main/jni/first-party/hermes/Android.mk" 134 // into "$thirdPartyNdkDir/hermes" 135 // + 136 // + rename '(.+).so', '$1_abi43_0_0.so' 137 // + } 138 // + exec { 139 // + commandLine("patchelf", "--set-soname", "libhermes_abi43_0_0.so", "$thirdPartyNdkDir/hermes/jni/arm64-v8a/libhermes_abi43_0_0.so") 140 // + } 141 // + exec { 142 // + commandLine("patchelf", "--set-soname", "libhermes_abi43_0_0.so", "$thirdPartyNdkDir/hermes/jni/armeabi-v7a/libhermes_abi43_0_0.so") 143 // + } 144 // + exec { 145 // + commandLine("patchelf", "--set-soname", "libhermes_abi43_0_0.so", "$thirdPartyNdkDir/hermes/jni/x86/libhermes_abi43_0_0.so") 146 // + } 147 // + exec { 148 // + commandLine("patchelf", "--set-soname", "libhermes_abi43_0_0.so", "$thirdPartyNdkDir/hermes/jni/x86_64/libhermes_abi43_0_0.so") 149 // + } 150 // } 151 // 152 // + 153 // task downloadGlog(dependsOn: createNativeDepsDirectories, type: Download) { 154 // src("https://github.com/google/glog/archive/v${GLOG_VERSION}.tar.gz") 155 // onlyIfNewer(true) 156 // ``` 157 await transformFileAsync(buildGradlePath, [ 158 { 159 // reset `prepareHermes` task from Copy type to generic type then we can do both copy and exec. 160 find: /^(task prepareHermes\(dependsOn: .+), type: Copy(\).+$)/m, 161 replaceWith: '$1$2', 162 }, 163 { 164 // wrap copy task and append exec tasks 165 find: /(^\s*def soFiles = zipTree\(hermesAAR\).+)\n([\s\S]+?)^\}/gm, 166 replaceWith: `\ 167$1 168 copy { 169 $2 170 rename '(.+).so', '$$1_abi${abiVersion}.so' 171 } 172 exec { 173 commandLine("patchelf", "--set-soname", "${versionedHermesLibName}", "$thirdPartyNdkDir/hermes/jni/arm64-v8a/${versionedHermesLibName}") 174 } 175 exec { 176 commandLine("patchelf", "--set-soname", "${versionedHermesLibName}", "$thirdPartyNdkDir/hermes/jni/armeabi-v7a/${versionedHermesLibName}") 177 } 178 exec { 179 commandLine("patchelf", "--set-soname", "${versionedHermesLibName}", "$thirdPartyNdkDir/hermes/jni/x86/${versionedHermesLibName}") 180 } 181 exec { 182 commandLine("patchelf", "--set-soname", "${versionedHermesLibName}", "$thirdPartyNdkDir/hermes/jni/x86_64/${versionedHermesLibName}") 183 } 184} 185`, 186 }, 187 ]); 188} 189