1import escapeRegExp from 'lodash/escapeRegExp';
2import path from 'path';
3
4import { transformString } from '../../Transforms';
5import { FileTransform, FileTransforms, StringTransform } from '../../Transforms.types';
6import { baseCmakeTransforms } from './cmakeTransforms';
7import { JniLibNames } from './libraries';
8import { packagesToRename } from './packagesConfig';
9
10function pathFromPkg(pkg: string): string {
11  return pkg.replace(/\./g, '/');
12}
13
14export function reactNativeTransforms(
15  versionedReactNativeRoot: string,
16  abiVersion: string
17): FileTransforms {
18  return {
19    path: [],
20    content: [
21      // Update codegen folder to our customized folder
22      {
23        paths: './ReactAndroid/build.gradle',
24        find: /"REACT_GENERATED_SRC_DIR=.+?",/,
25        replaceWith: `"REACT_GENERATED_SRC_DIR=${versionedReactNativeRoot}",`,
26      },
27      // Add generated java to sourceSets
28      {
29        paths: './ReactAndroid/build.gradle',
30        find: /(\bsrcDirs = \["src\/main\/java",.+)(\])/,
31        replaceWith: `$1, "${path.join(versionedReactNativeRoot, 'codegen')}/java"$2`,
32      },
33      // Disable codegen plugin
34      {
35        paths: './ReactAndroid/build.gradle',
36        find: /(\bid\("com\.facebook\.react"\)$)/m,
37        replaceWith: '// $1',
38      },
39      {
40        paths: './ReactAndroid/build.gradle',
41        find: /(^react {[^]+?\n\})/m,
42        replaceWith: '/* $1 */',
43      },
44      {
45        paths: './ReactAndroid/build.gradle',
46        find: /(\b(preBuild\.)?dependsOn\("generateCodegenArtifactsFromSchema"\))/g,
47        replaceWith: '// $1',
48      },
49      {
50        paths: './ReactAndroid/build.gradle',
51        find: 'new File(buildDir, "generated/source/codegen/jni/").absolutePath',
52        replaceWith: '"../codegen/jni/"',
53      },
54      {
55        paths: './ReactAndroid/build.gradle',
56        find: /(externalNativeBuild\s*\{)([\s\S]*?)(\}\s)/g,
57        replaceWith: (_, p1, p2, p3) =>
58          [
59            p1,
60            transformString(
61              p2,
62              JniLibNames.map((lib: string) => ({
63                find: new RegExp(`"${escapeRegExp(lib)}"`, 'g'),
64                replaceWith: `"${lib}_${abiVersion}"`,
65              }))
66            ),
67            p3,
68          ].join(''),
69      },
70      {
71        paths: './ReactAndroid/build.gradle',
72        find: /(    prefab\s*\{)([\s\S]*?)(^    \}\s)/gm,
73        replaceWith: (_, p1, p2, p3) =>
74          [
75            p1,
76            transformString(
77              p2,
78              JniLibNames.map((lib: string) => ({
79                find: new RegExp(`\\b${escapeRegExp(lib)}\\s+?\\{`, 'g'),
80                replaceWith: `${lib}_${abiVersion} {`,
81              }))
82            ),
83            p3,
84          ].join(''),
85      },
86      ...packagesToRename.map((pkg: string) => ({
87        paths: ['./ReactCommon/**/*.{java,h,cpp}', './ReactAndroid/src/main/**/*.{java,h,cpp}'],
88        find: new RegExp(`${escapeRegExp(pathFromPkg(pkg))}`, 'g'),
89        replaceWith: `${abiVersion}/${pathFromPkg(pkg)}`,
90      })),
91      ...reactNativeCmakeTransforms(abiVersion),
92      {
93        paths: './ReactAndroid/hermes-engine/build.gradle',
94        find: 'libraryName "libhermes"',
95        replaceWith: `libraryName "libhermes_${abiVersion}"`,
96      },
97      {
98        paths: './ReactAndroid/hermes-engine/build.gradle',
99        find: /(prefab {\s+libhermes)/,
100        replaceWith: `$1_${abiVersion}`,
101      },
102      {
103        paths: './ReactAndroid/hermes-engine/build.gradle',
104        find: 'targets "libhermes"',
105        replaceWith: `targets "libhermes_${abiVersion}"`,
106      },
107      ...[...JniLibNames, 'fb', 'fbjni'].map((libName) => ({
108        paths: '*.java',
109        find: new RegExp(`SoLoader.loadLibrary\\\("${escapeRegExp(libName)}"\\\)`),
110        replaceWith: `SoLoader.loadLibrary("${libName}_${abiVersion}")`,
111      })),
112      // add HERMES_ENABLE_DEBUGGER for libhermes-executor-release.so
113      {
114        paths: './ReactAndroid/hermes-engine/build.gradle',
115        find: /-DHERMES_ENABLE_DEBUGGER=False/,
116        replaceWith: '-DHERMES_ENABLE_DEBUGGER=True',
117      },
118      {
119        paths: './ReactAndroid/hermes-engine/build.gradle',
120        find: /\b((configureBuildForHermes|prepareHeadersForPrefab)\.dependsOn\(unzipHermes\))/g,
121        replaceWith: '// $1',
122      },
123      {
124        paths: './ReactCommon/hermes/executor/CMakeLists.txt',
125        find: /\bdebug (hermes-inspector_)/g,
126        replaceWith: '$1',
127      },
128      {
129        paths: './ReactCommon/hermes/executor/CMakeLists.txt',
130        find: /if\(\${CMAKE_BUILD_TYPE} MATCHES Debug\)(\n\s*target_compile_options)/g,
131        replaceWith: 'if(true)$1',
132      },
133      {
134        paths: './ReactAndroid/src/main/jni/react/hermes/reactexecutor/CMakeLists.txt',
135        find: '$<$<CONFIG:Debug>:-DHERMES_ENABLE_DEBUGGER=1>',
136        replaceWith: '-DHERMES_ENABLE_DEBUGGER=1',
137      },
138      {
139        // workaround build dependency issue to explicitly link hermes_executor_common to hermes_executor
140        // originally, it's hermes_inspector -> hermes_executor_common -> hermes_executor
141        paths: './ReactAndroid/src/main/jni/react/hermes/reactexecutor/CMakeLists.txt',
142        find: /^(\s+hermes_executor_common.*)$/m,
143        replaceWith: `$1\n        hermes_inspector_${abiVersion}`,
144      },
145    ],
146  };
147}
148
149export function codegenTransforms(abiVersion: string): FileTransforms {
150  return {
151    path: [],
152    content: [
153      ...packagesToRename.map((pkg: string) => ({
154        paths: ['**/*.{java,h,cpp}'],
155        find: new RegExp(`${escapeRegExp(pathFromPkg(pkg))}`, 'g'),
156        replaceWith: `${abiVersion}/${pathFromPkg(pkg)}`,
157      })),
158      ...reactNativeCmakeTransforms(abiVersion),
159    ],
160  };
161}
162
163function reactNativeCmakeTransforms(abiVersion: string): FileTransform[] {
164  const libNames = JniLibNames.map((lib: string): string =>
165    lib.startsWith('lib') ? lib.slice(3) : lib
166  ).filter((lib: string) => !['fbjni'].includes(lib));
167  libNames.push('${HERMES_TARGET_NAME}'); // variable used in hermes-executor CMakeLists.txt
168  libNames.push('hermes-engine::libhermes');
169
170  return [
171    ...baseCmakeTransforms(abiVersion, libNames).map((transform: StringTransform) => ({
172      paths: 'CMakeLists.txt',
173      ...transform,
174    })),
175    {
176      paths: 'CMakeLists.txt',
177      find: 'add_react_build_subdir(generated/source/codegen/jni)',
178      replaceWith: 'add_react_android_subdir(../codegen/jni)',
179    },
180    {
181      paths: 'CMakeLists.txt',
182      find: /libhermes\.so/g,
183      replaceWith: `libhermes_${abiVersion}.so`,
184    },
185  ];
186}
187
188export function hermesTransforms(abiVersion: string): StringTransform[] {
189  return [
190    {
191      find: /OUTPUT_NAME hermes/g,
192      replaceWith: `OUTPUT_NAME hermes_${abiVersion}`,
193    },
194    {
195      find: /libhermes/g,
196      replaceWith: `libhermes_${abiVersion}`,
197    },
198    {
199      find: /jsi/g,
200      replaceWith: `jsi_${abiVersion}`,
201    },
202  ];
203}
204