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