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: /(\bpreBuild\.dependsOn\("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 '--platform', 83 'android', 84 '--schemaPath', 85 schemaOutputPath, 86 '--outputDir', 87 codegenOutputRoot, 88 '--libraryName', 89 'rncore', 90 '--javaPackageName', 91 'com.facebook.fbreact.specs', 92 ]); 93} 94 95export async function renameHermesEngine(versionedReactAndroidPath: string, version: string) { 96 const abiVersion = version.replace(/\./g, '_'); 97 const abiName = `abi${abiVersion}`; 98 const prebuiltHermesMkPath = path.join( 99 versionedReactAndroidPath, 100 'src', 101 'main', 102 'jni', 103 'first-party', 104 'hermes', 105 'Android.mk' 106 ); 107 const versionedHermesLibName = `libhermes_${abiName}.so`; 108 await transformFileAsync(prebuiltHermesMkPath, [ 109 { 110 find: /^(LOCAL_SRC_FILES\s+:=\s+jni\/\$\(TARGET_ARCH_ABI\))\/libhermes.so$/gm, 111 replaceWith: `$1/${versionedHermesLibName}`, 112 }, 113 ]); 114 115 const buildGradlePath = path.join(versionedReactAndroidPath, 'build.gradle'); 116 // patch prepareHermes task to rename copied library and update soname 117 // the diff is something like that: 118 // 119 // ```diff 120 // --- android/versioned-react-native/ReactAndroid/build.gradle.orig 2021-08-14 00:40:18.000000000 +0800 121 // +++ android/versioned-react-native/ReactAndroid/build.gradle 2021-08-14 00:40:58.000000000 +0800 122 // @@ -114,7 +114,7 @@ 123 // into("$thirdPartyNdkDir/folly") 124 // } 125 // 126 // -task prepareHermes(dependsOn: createNativeDepsDirectories, type: Copy) { 127 // +task prepareHermes(dependsOn: createNativeDepsDirectories) { 128 // def hermesPackagePath = findNodeModulePath(projectDir, "hermes-engine") 129 // if (!hermesPackagePath) { 130 // throw new GradleScriptException("Could not find the hermes-engine npm package", null) 131 // @@ -126,12 +126,29 @@ 132 // } 133 // 134 // def soFiles = zipTree(hermesAAR).matching({ it.include "**/*.so" }) 135 // - 136 // + copy { 137 // + 138 // from soFiles 139 // from "src/main/jni/first-party/hermes/Android.mk" 140 // into "$thirdPartyNdkDir/hermes" 141 // + 142 // + rename '(.+).so', '$1_abi43_0_0.so' 143 // + } 144 // + exec { 145 // + commandLine("patchelf", "--set-soname", "libhermes_abi43_0_0.so", "$thirdPartyNdkDir/hermes/jni/arm64-v8a/libhermes_abi43_0_0.so") 146 // + } 147 // + exec { 148 // + commandLine("patchelf", "--set-soname", "libhermes_abi43_0_0.so", "$thirdPartyNdkDir/hermes/jni/armeabi-v7a/libhermes_abi43_0_0.so") 149 // + } 150 // + exec { 151 // + commandLine("patchelf", "--set-soname", "libhermes_abi43_0_0.so", "$thirdPartyNdkDir/hermes/jni/x86/libhermes_abi43_0_0.so") 152 // + } 153 // + exec { 154 // + commandLine("patchelf", "--set-soname", "libhermes_abi43_0_0.so", "$thirdPartyNdkDir/hermes/jni/x86_64/libhermes_abi43_0_0.so") 155 // + } 156 // } 157 // 158 // + 159 // task downloadGlog(dependsOn: createNativeDepsDirectories, type: Download) { 160 // src("https://github.com/google/glog/archive/v${GLOG_VERSION}.tar.gz") 161 // onlyIfNewer(true) 162 // ``` 163 await transformFileAsync(buildGradlePath, [ 164 { 165 // reset `prepareHermes` task from Copy type to generic type then we can do both copy and exec. 166 find: /^(task prepareHermes\(dependsOn: .+), type: Copy(\).+$)/m, 167 replaceWith: '$1$2', 168 }, 169 { 170 // wrap copy task and append exec tasks 171 find: /(^\s*def soFiles = zipTree\(hermesAAR\).+)\n([\s\S]+?)^\}/gm, 172 replaceWith: `\ 173$1 174 copy { 175 $2 176 rename '(.+).so', '$$1_abi${abiVersion}.so' 177 } 178 exec { 179 commandLine("patchelf", "--set-soname", "${versionedHermesLibName}", "$thirdPartyNdkDir/hermes/jni/arm64-v8a/${versionedHermesLibName}") 180 } 181 exec { 182 commandLine("patchelf", "--set-soname", "${versionedHermesLibName}", "$thirdPartyNdkDir/hermes/jni/armeabi-v7a/${versionedHermesLibName}") 183 } 184 exec { 185 commandLine("patchelf", "--set-soname", "${versionedHermesLibName}", "$thirdPartyNdkDir/hermes/jni/x86/${versionedHermesLibName}") 186 } 187 exec { 188 commandLine("patchelf", "--set-soname", "${versionedHermesLibName}", "$thirdPartyNdkDir/hermes/jni/x86_64/${versionedHermesLibName}") 189 } 190} 191`, 192 }, 193 ]); 194} 195