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      // Add generated java to sourceSets
22      {
23        paths: './ReactAndroid/build.gradle',
24        find: /(\bsrcDirs = \["src\/main\/java",.+)(\])/,
25        replaceWith: `$1, "${path.join(versionedReactNativeRoot, 'codegen')}/java"$2`,
26      },
27      // Disable codegen plugin
28      {
29        paths: './ReactAndroid/build.gradle',
30        find: /(\bid\("com\.facebook\.react"\)$)/m,
31        replaceWith: '// $1',
32      },
33      {
34        paths: './ReactAndroid/build.gradle',
35        find: /(^react {[^]+?\n\})/m,
36        replaceWith: '/* $1 */',
37      },
38      {
39        paths: './ReactAndroid/build.gradle',
40        find: /(\b(preBuild\.)?dependsOn\("generateCodegenArtifactsFromSchema"\))/g,
41        replaceWith: '// $1',
42      },
43      {
44        paths: './ReactAndroid/build.gradle',
45        find: 'new File(buildDir, "generated/source/codegen/jni/").absolutePath',
46        replaceWith: '"../codegen/jni/"',
47      },
48      {
49        paths: './ReactAndroid/build.gradle',
50        find: /(externalNativeBuild\s*\{)([\s\S]*?)(\}\s)/g,
51        replaceWith: (_, p1, p2, p3) =>
52          [
53            p1,
54            transformString(
55              p2,
56              JniLibNames.map((lib: string) => ({
57                find: new RegExp(`"${escapeRegExp(lib)}"`, 'g'),
58                replaceWith: `"${lib}_${abiVersion}"`,
59              }))
60            ),
61            p3,
62          ].join(''),
63      },
64      {
65        paths: './ReactAndroid/build.gradle',
66        find: /(    prefab\s*\{)([\s\S]*?)(^    \}\s)/gm,
67        replaceWith: (_, p1, p2, p3) =>
68          [
69            p1,
70            transformString(
71              p2,
72              JniLibNames.map((lib: string) => ({
73                find: new RegExp(`\\b${escapeRegExp(lib)}\\s+?\\{`, 'g'),
74                replaceWith: `${lib}_${abiVersion} {`,
75              }))
76            ),
77            p3,
78          ].join(''),
79      },
80      ...packagesToRename.map((pkg: string) => ({
81        paths: [
82          './ReactCommon/**/*.{java,kt,h,cpp}',
83          './ReactAndroid/src/main/**/*.{java,kt,h,cpp}',
84        ],
85        find: new RegExp(`${escapeRegExp(pathFromPkg(pkg))}`, 'g'),
86        replaceWith: `${abiVersion}/${pathFromPkg(pkg)}`,
87      })),
88      ...reactNativeCmakeTransforms(abiVersion),
89      {
90        paths: './ReactAndroid/hermes-engine/build.gradle',
91        find: 'libraryName "libhermes"',
92        replaceWith: `libraryName "libhermes_${abiVersion}"`,
93      },
94      {
95        paths: './ReactAndroid/hermes-engine/build.gradle',
96        find: /(prefab {\s+libhermes)/,
97        replaceWith: `$1_${abiVersion}`,
98      },
99      {
100        paths: './ReactAndroid/hermes-engine/build.gradle',
101        find: 'targets "libhermes"',
102        replaceWith: `targets "libhermes_${abiVersion}"`,
103      },
104      ...[...JniLibNames, 'fb', 'fbjni'].map((libName) => ({
105        paths: '*.{java,kt}',
106        find: new RegExp(`SoLoader.loadLibrary\\\("${escapeRegExp(libName)}"\\\)`),
107        replaceWith: `SoLoader.loadLibrary("${libName}_${abiVersion}")`,
108      })),
109      // add HERMES_ENABLE_DEBUGGER for libhermes-executor-release.so
110      {
111        paths: './ReactAndroid/hermes-engine/build.gradle',
112        find: /-DHERMES_ENABLE_DEBUGGER=False/,
113        replaceWith: '-DHERMES_ENABLE_DEBUGGER=True',
114      },
115      {
116        paths: './ReactAndroid/hermes-engine/build.gradle',
117        find: /\b((configureBuildForHermes|prepareHeadersForPrefab)\.dependsOn\(unzipHermes\))/g,
118        replaceWith: '// $1',
119      },
120      {
121        paths: './ReactCommon/hermes/executor/CMakeLists.txt',
122        find: /\bdebug (hermes-inspector_)/g,
123        replaceWith: '$1',
124      },
125      {
126        paths: [
127          './ReactCommon/hermes/executor/CMakeLists.txt',
128          './ReactCommon/hermes/inspector/CMakeLists.txt',
129        ],
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,kt,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