@react-native/gradle-plugin 0.72.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +16 -0
  2. package/build.gradle.kts +68 -0
  3. package/gradle/wrapper/gradle-wrapper.jar +0 -0
  4. package/gradle/wrapper/gradle-wrapper.properties +5 -0
  5. package/gradlew +234 -0
  6. package/gradlew.bat +89 -0
  7. package/package.json +28 -0
  8. package/settings.gradle.kts +16 -0
  9. package/src/main/kotlin/com/facebook/react/ReactExtension.kt +151 -0
  10. package/src/main/kotlin/com/facebook/react/ReactPlugin.kt +163 -0
  11. package/src/main/kotlin/com/facebook/react/TaskConfiguration.kt +76 -0
  12. package/src/main/kotlin/com/facebook/react/model/ModelCodegenConfig.kt +15 -0
  13. package/src/main/kotlin/com/facebook/react/model/ModelCodegenConfigAndroid.kt +10 -0
  14. package/src/main/kotlin/com/facebook/react/model/ModelPackageJson.kt +10 -0
  15. package/src/main/kotlin/com/facebook/react/tasks/BuildCodegenCLITask.kt +58 -0
  16. package/src/main/kotlin/com/facebook/react/tasks/BundleHermesCTask.kt +190 -0
  17. package/src/main/kotlin/com/facebook/react/tasks/GenerateCodegenArtifactsTask.kt +81 -0
  18. package/src/main/kotlin/com/facebook/react/tasks/GenerateCodegenSchemaTask.kt +80 -0
  19. package/src/main/kotlin/com/facebook/react/tasks/internal/PrepareBoostTask.kt +46 -0
  20. package/src/main/kotlin/com/facebook/react/tasks/internal/PrepareGlogTask.kt +79 -0
  21. package/src/main/kotlin/com/facebook/react/tasks/internal/PrepareJSCTask.kt +50 -0
  22. package/src/main/kotlin/com/facebook/react/tasks/internal/PrepareLibeventTask.kt +51 -0
  23. package/src/main/kotlin/com/facebook/react/tasks/internal/PreparePrefabHeadersTask.kt +62 -0
  24. package/src/main/kotlin/com/facebook/react/tasks/internal/utils/PrefabPreprocessingEntry.kt +27 -0
  25. package/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt +54 -0
  26. package/src/main/kotlin/com/facebook/react/utils/BackwardCompatUtils.kt +48 -0
  27. package/src/main/kotlin/com/facebook/react/utils/DependencyUtils.kt +89 -0
  28. package/src/main/kotlin/com/facebook/react/utils/FileUtils.kt +20 -0
  29. package/src/main/kotlin/com/facebook/react/utils/JsonUtils.kt +21 -0
  30. package/src/main/kotlin/com/facebook/react/utils/NdkConfiguratorUtils.kt +139 -0
  31. package/src/main/kotlin/com/facebook/react/utils/Os.kt +31 -0
  32. package/src/main/kotlin/com/facebook/react/utils/PathUtils.kt +220 -0
  33. package/src/main/kotlin/com/facebook/react/utils/ProjectUtils.kt +46 -0
  34. package/src/main/kotlin/com/facebook/react/utils/TaskUtils.kt +28 -0
@@ -0,0 +1,139 @@
1
+ /*
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ package com.facebook.react.utils
9
+
10
+ import com.android.build.api.variant.AndroidComponentsExtension
11
+ import com.android.build.api.variant.Variant
12
+ import com.facebook.react.ReactExtension
13
+ import com.facebook.react.utils.ProjectUtils.isNewArchEnabled
14
+ import java.io.File
15
+ import org.gradle.api.Project
16
+
17
+ internal object NdkConfiguratorUtils {
18
+ @Suppress("UnstableApiUsage")
19
+ fun configureReactNativeNdk(project: Project, extension: ReactExtension) {
20
+ project.pluginManager.withPlugin("com.android.application") {
21
+ project.extensions.getByType(AndroidComponentsExtension::class.java).finalizeDsl { ext ->
22
+ if (!project.isNewArchEnabled) {
23
+ // For Old Arch, we don't need to setup the NDK
24
+ return@finalizeDsl
25
+ }
26
+ // We enable prefab so users can consume .so/headers from ReactAndroid and hermes-engine
27
+ // .aar
28
+ ext.buildFeatures.prefab = true
29
+
30
+ // If the user has not provided a CmakeLists.txt path, let's provide
31
+ // the default one from the framework
32
+ if (ext.externalNativeBuild.cmake.path == null) {
33
+ ext.externalNativeBuild.cmake.path =
34
+ File(
35
+ extension.reactNativeDir.get().asFile,
36
+ "ReactAndroid/cmake-utils/default-app-setup/CMakeLists.txt")
37
+ }
38
+
39
+ // Parameters should be provided in an additive manner (do not override what
40
+ // the user provided, but allow for sensible defaults).
41
+ val cmakeArgs = ext.defaultConfig.externalNativeBuild.cmake.arguments
42
+ if ("-DPROJECT_BUILD_DIR" !in cmakeArgs) {
43
+ cmakeArgs.add("-DPROJECT_BUILD_DIR=${project.buildDir}")
44
+ }
45
+ if ("-DREACT_ANDROID_DIR" !in cmakeArgs) {
46
+ cmakeArgs.add(
47
+ "-DREACT_ANDROID_DIR=${extension.reactNativeDir.file("ReactAndroid").get().asFile}")
48
+ }
49
+ if ("-DANDROID_STL" !in cmakeArgs) {
50
+ cmakeArgs.add("-DANDROID_STL=c++_shared")
51
+ }
52
+ }
53
+ }
54
+ }
55
+
56
+ /**
57
+ * This method is used to configure the .so Packaging Options for the given variant. It will make
58
+ * sure we specify the correct .pickFirsts for all the .so files we are producing or that we're
59
+ * aware of as some of our dependencies are pulling them in.
60
+ */
61
+ fun configureNewArchPackagingOptions(
62
+ project: Project,
63
+ variant: Variant,
64
+ ) {
65
+ if (!project.isNewArchEnabled) {
66
+ // For Old Arch, we set a pickFirst only on libraries that we know are
67
+ // clashing with our direct dependencies (FBJNI, Flipper and Hermes).
68
+ variant.packaging.jniLibs.pickFirsts.addAll(
69
+ listOf(
70
+ "**/libfbjni.so",
71
+ "**/libc++_shared.so",
72
+ ))
73
+ } else {
74
+ // We set some packagingOptions { pickFirst ... } for our users for libraries we own.
75
+ variant.packaging.jniLibs.pickFirsts.addAll(
76
+ listOf(
77
+ // This is the .so provided by FBJNI via prefab
78
+ "**/libfbjni.so",
79
+ // Those are prefab libraries we distribute via ReactAndroid
80
+ // Due to a bug in AGP, they fire a warning on console as both the JNI
81
+ // and the prefab .so files gets considered. See more on:
82
+ "**/libfabricjni.so",
83
+ "**/libfolly_runtime.so",
84
+ "**/libglog.so",
85
+ "**/libjsi.so",
86
+ "**/libreact_codegen_rncore.so",
87
+ "**/libreact_debug.so",
88
+ "**/libreact_nativemodule_core.so",
89
+ "**/libreact_newarchdefaults.so",
90
+ "**/libreact_render_componentregistry.so",
91
+ "**/libreact_render_core.so",
92
+ "**/libreact_render_debug.so",
93
+ "**/libreact_render_graphics.so",
94
+ "**/libreact_render_imagemanager.so",
95
+ "**/libreact_render_mapbuffer.so",
96
+ "**/librrc_image.so",
97
+ "**/librrc_view.so",
98
+ "**/libruntimeexecutor.so",
99
+ "**/libturbomodulejsijni.so",
100
+ "**/libyoga.so",
101
+ // AGP will give priority of libc++_shared coming from App modules.
102
+ "**/libc++_shared.so",
103
+ ))
104
+ }
105
+ }
106
+
107
+ /**
108
+ * This method is used to configure the .so Cleanup for the given variant. It takes care of
109
+ * cleaning up the .so files that are not needed for Hermes or JSC, given a specific variant.
110
+ */
111
+ fun configureJsEnginePackagingOptions(
112
+ config: ReactExtension,
113
+ variant: Variant,
114
+ hermesEnabled: Boolean,
115
+ ) {
116
+ if (config.enableSoCleanup.get()) {
117
+ val (excludes, includes) = getPackagingOptionsForVariant(hermesEnabled)
118
+ variant.packaging.jniLibs.excludes.addAll(excludes)
119
+ variant.packaging.jniLibs.pickFirsts.addAll(includes)
120
+ }
121
+ }
122
+
123
+ fun getPackagingOptionsForVariant(hermesEnabled: Boolean): Pair<List<String>, List<String>> {
124
+ val excludes = mutableListOf<String>()
125
+ val includes = mutableListOf<String>()
126
+ if (hermesEnabled) {
127
+ excludes.add("**/libjsc.so")
128
+ excludes.add("**/libjscexecutor.so")
129
+ includes.add("**/libhermes.so")
130
+ includes.add("**/libhermes_executor.so")
131
+ } else {
132
+ excludes.add("**/libhermes.so")
133
+ excludes.add("**/libhermes_executor.so")
134
+ includes.add("**/libjsc.so")
135
+ includes.add("**/libjscexecutor.so")
136
+ }
137
+ return excludes to includes
138
+ }
139
+ }
@@ -0,0 +1,31 @@
1
+ /*
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ package com.facebook.react.utils
9
+
10
+ object Os {
11
+
12
+ fun isWindows(): Boolean =
13
+ System.getProperty("os.name")?.lowercase()?.contains("windows") ?: false
14
+
15
+ fun isMac(): Boolean = System.getProperty("os.name")?.lowercase()?.contains("mac") ?: false
16
+
17
+ fun isLinuxAmd64(): Boolean {
18
+ val osNameMatch = System.getProperty("os.name")?.lowercase()?.contains("linux") ?: false
19
+ val archMatch = System.getProperty("os.arch")?.lowercase()?.contains("amd64") ?: false
20
+ return osNameMatch && archMatch
21
+ }
22
+
23
+ fun String.unixifyPath() =
24
+ this.replace('\\', '/').replace(":", "").let {
25
+ if (!it.startsWith("/")) {
26
+ "/$it"
27
+ } else {
28
+ it
29
+ }
30
+ }
31
+ }
@@ -0,0 +1,220 @@
1
+ /*
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ @file:JvmName("PathUtils")
9
+
10
+ package com.facebook.react.utils
11
+
12
+ import com.facebook.react.ReactExtension
13
+ import com.facebook.react.model.ModelPackageJson
14
+ import java.io.File
15
+ import org.gradle.api.Project
16
+
17
+ /**
18
+ * Computes the entry file for React Native. The Algo follows this order:
19
+ * 1. The file pointed by the ENTRY_FILE env variable, if set.
20
+ * 2. The file provided by the `entryFile` config in the `reactApp` Gradle extension
21
+ * 3. The `index.android.js` file, if available.
22
+ * 4. Fallback to the `index.js` file.
23
+ *
24
+ * @param config The [ReactExtension] configured for this project
25
+ */
26
+ internal fun detectedEntryFile(config: ReactExtension): File =
27
+ detectEntryFile(
28
+ entryFile = config.entryFile.orNull?.asFile, reactRoot = config.root.get().asFile)
29
+
30
+ /**
31
+ * Computes the CLI file for React Native. The Algo follows this order:
32
+ * 1. The path provided by the `cliFile` config in the `react {}` Gradle extension
33
+ * 2. The output of `node --print "require.resolve('react-native/cli');"` if not failing.
34
+ * 3. The `node_modules/react-native/cli.js` file if exists
35
+ * 4. Fails otherwise
36
+ */
37
+ internal fun detectedCliFile(config: ReactExtension): File =
38
+ detectCliFile(
39
+ reactNativeRoot = config.root.get().asFile,
40
+ preconfiguredCliFile = config.cliFile.asFile.orNull)
41
+
42
+ /**
43
+ * Computes the `hermesc` command location. The Algo follows this order:
44
+ * 1. The path provided by the `hermesCommand` config in the `react` Gradle extension
45
+ * 2. The file located in `node_modules/react-native/sdks/hermes/build/bin/hermesc`. This will be
46
+ * used if the user is building Hermes from source.
47
+ * 3. The file located in `node_modules/react-native/sdks/hermesc/%OS-BIN%/hermesc` where `%OS-BIN%`
48
+ * is substituted with the correct OS arch. This will be used if the user is using a precompiled
49
+ * hermes-engine package.
50
+ * 4. Fails otherwise
51
+ */
52
+ internal fun detectedHermesCommand(config: ReactExtension): String =
53
+ detectOSAwareHermesCommand(config.root.get().asFile, config.hermesCommand.get())
54
+
55
+ private fun detectEntryFile(entryFile: File?, reactRoot: File): File =
56
+ when {
57
+ System.getenv("ENTRY_FILE") != null -> File(System.getenv("ENTRY_FILE"))
58
+ entryFile != null -> entryFile
59
+ File(reactRoot, "index.android.js").exists() -> File(reactRoot, "index.android.js")
60
+ else -> File(reactRoot, "index.js")
61
+ }
62
+
63
+ private fun detectCliFile(reactNativeRoot: File, preconfiguredCliFile: File?): File {
64
+ // 1. preconfigured path
65
+ if (preconfiguredCliFile != null) {
66
+ if (preconfiguredCliFile.exists()) {
67
+ return preconfiguredCliFile
68
+ }
69
+ }
70
+
71
+ // 2. node module path
72
+ val nodeProcess =
73
+ Runtime.getRuntime()
74
+ .exec(
75
+ arrayOf("node", "--print", "require.resolve('react-native/cli');"),
76
+ emptyArray(),
77
+ reactNativeRoot)
78
+
79
+ val nodeProcessOutput = nodeProcess.inputStream.use { it.bufferedReader().readText().trim() }
80
+
81
+ if (nodeProcessOutput.isNotEmpty()) {
82
+ val nodeModuleCliJs = File(nodeProcessOutput)
83
+ if (nodeModuleCliJs.exists()) {
84
+ return nodeModuleCliJs
85
+ }
86
+ }
87
+
88
+ // 3. cli.js in the root folder
89
+ val rootCliJs = File(reactNativeRoot, "node_modules/react-native/cli.js")
90
+ if (rootCliJs.exists()) {
91
+ return rootCliJs
92
+ }
93
+
94
+ error(
95
+ """
96
+ Couldn't determine CLI location!
97
+
98
+ Please set `react { cliFile = file(...) }` inside your
99
+ build.gradle to the path of the react-native cli.js file.
100
+ This file typically resides in `node_modules/react-native/cli.js`
101
+ """
102
+ .trimIndent())
103
+ }
104
+
105
+ /**
106
+ * Computes the `hermesc` command location. The Algo follows this order:
107
+ * 1. The path provided by the `hermesCommand` config in the `react` Gradle extension
108
+ * 2. The file located in `node_modules/react-native/sdks/hermes/build/bin/hermesc`. This will be
109
+ * used if the user is building Hermes from source.
110
+ * 3. The file located in `node_modules/react-native/sdks/hermesc/%OS-BIN%/hermesc` where `%OS-BIN%`
111
+ * is substituted with the correct OS arch. This will be used if the user is using a precompiled
112
+ * hermes-engine package.
113
+ * 4. Fails otherwise
114
+ */
115
+ internal fun detectOSAwareHermesCommand(projectRoot: File, hermesCommand: String): String {
116
+ // 1. If the project specifies a Hermes command, don't second guess it.
117
+ if (hermesCommand.isNotBlank()) {
118
+ val osSpecificHermesCommand =
119
+ if ("%OS-BIN%" in hermesCommand) {
120
+ hermesCommand.replace("%OS-BIN%", getHermesOSBin())
121
+ } else {
122
+ hermesCommand
123
+ }
124
+ return osSpecificHermesCommand
125
+ // Execution on Windows fails with / as separator
126
+ .replace('/', File.separatorChar)
127
+ }
128
+
129
+ // 2. If the project is building hermes-engine from source, use hermesc from there
130
+ val builtHermesc =
131
+ getBuiltHermescFile(projectRoot, System.getenv("REACT_NATIVE_OVERRIDE_HERMES_DIR"))
132
+ if (builtHermesc.exists()) {
133
+ return builtHermesc.absolutePath
134
+ }
135
+
136
+ // 3. If the react-native contains a pre-built hermesc, use it.
137
+ val prebuiltHermesPath =
138
+ HERMESC_IN_REACT_NATIVE_DIR.plus(getHermesCBin())
139
+ .replace("%OS-BIN%", getHermesOSBin())
140
+ // Execution on Windows fails with / as separator
141
+ .replace('/', File.separatorChar)
142
+
143
+ val prebuiltHermes = File(projectRoot, prebuiltHermesPath)
144
+ if (prebuiltHermes.exists()) {
145
+ return prebuiltHermes.absolutePath
146
+ }
147
+
148
+ error(
149
+ "Couldn't determine Hermesc location. " +
150
+ "Please set `react.hermesCommand` to the path of the hermesc binary file. " +
151
+ "node_modules/react-native/sdks/hermesc/%OS-BIN%/hermesc")
152
+ }
153
+
154
+ /**
155
+ * Gets the location where Hermesc should be. If nothing is specified, built hermesc is assumed to
156
+ * be inside [HERMESC_BUILT_FROM_SOURCE_DIR]. Otherwise user can specify an override with
157
+ * [pathOverride], which is assumed to be an absolute path where Hermes source code is
158
+ * provided/built.
159
+ *
160
+ * @param projectRoot The root of the Project.
161
+ */
162
+ internal fun getBuiltHermescFile(projectRoot: File, pathOverride: String?) =
163
+ if (!pathOverride.isNullOrBlank()) {
164
+ File(pathOverride, "build/bin/${getHermesCBin()}")
165
+ } else {
166
+ File(projectRoot, HERMESC_BUILT_FROM_SOURCE_DIR.plus(getHermesCBin()))
167
+ }
168
+
169
+ internal fun getHermesCBin() = if (Os.isWindows()) "hermesc.exe" else "hermesc"
170
+
171
+ internal fun getHermesOSBin(): String {
172
+ if (Os.isWindows()) return "win64-bin"
173
+ if (Os.isMac()) return "osx-bin"
174
+ if (Os.isLinuxAmd64()) return "linux64-bin"
175
+ error(
176
+ "OS not recognized. Please set project.react.hermesCommand " +
177
+ "to the path of a working Hermes compiler.")
178
+ }
179
+
180
+ internal fun projectPathToLibraryName(projectPath: String): String =
181
+ projectPath
182
+ .split(':', '-', '_', '.')
183
+ .joinToString("") { token -> token.replaceFirstChar { it.uppercase() } }
184
+ .plus("Spec")
185
+
186
+ /**
187
+ * Function to look for the relevant `package.json`. We first look in the parent folder of this
188
+ * Gradle module (generally the case for library projects) or we fallback to looking into the `root`
189
+ * folder of a React Native project (generally the case for app projects).
190
+ */
191
+ internal fun findPackageJsonFile(project: Project, extension: ReactExtension): File? {
192
+ val inParent = project.file("../package.json")
193
+ if (inParent.exists()) {
194
+ return inParent
195
+ }
196
+
197
+ val fromExtension = extension.root.file("package.json").orNull?.asFile
198
+ if (fromExtension?.exists() == true) {
199
+ return fromExtension
200
+ }
201
+
202
+ return null
203
+ }
204
+
205
+ /**
206
+ * Function to look for the `package.json` and parse it. It returns a [ModelPackageJson] if found or
207
+ * null others.
208
+ *
209
+ * Please note that this function access the [ReactExtension] field properties and calls .get() on
210
+ * them, so calling this during apply() of the ReactPlugin is not recommended. It should be invoked
211
+ * inside lazy lambdas or at execution time.
212
+ */
213
+ internal fun readPackageJsonFile(project: Project, extension: ReactExtension): ModelPackageJson? {
214
+ val packageJson = findPackageJsonFile(project, extension)
215
+ return packageJson?.let { JsonUtils.fromCodegenJson(it) }
216
+ }
217
+
218
+ private const val HERMESC_IN_REACT_NATIVE_DIR = "node_modules/react-native/sdks/hermesc/%OS-BIN%/"
219
+ private const val HERMESC_BUILT_FROM_SOURCE_DIR =
220
+ "node_modules/react-native/ReactAndroid/hermes-engine/build/hermes/bin/"
@@ -0,0 +1,46 @@
1
+ /*
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ package com.facebook.react.utils
9
+
10
+ import com.facebook.react.ReactExtension
11
+ import com.facebook.react.model.ModelPackageJson
12
+ import org.gradle.api.Project
13
+
14
+ internal object ProjectUtils {
15
+ internal val Project.isNewArchEnabled: Boolean
16
+ get() =
17
+ project.hasProperty("newArchEnabled") &&
18
+ project.property("newArchEnabled").toString().toBoolean()
19
+
20
+ const val HERMES_FALLBACK = true
21
+
22
+ internal val Project.isHermesEnabled: Boolean
23
+ get() =
24
+ if (project.hasProperty("hermesEnabled")) {
25
+ project.property("hermesEnabled").toString().lowercase().toBooleanStrictOrNull() ?: true
26
+ } else if (project.extensions.extraProperties.has("react")) {
27
+ @Suppress("UNCHECKED_CAST")
28
+ val reactMap = project.extensions.extraProperties.get("react") as? Map<String, Any?>
29
+ when (val enableHermesKey = reactMap?.get("enableHermes")) {
30
+ is Boolean -> enableHermesKey
31
+ is String -> enableHermesKey.lowercase().toBooleanStrictOrNull() ?: true
32
+ else -> HERMES_FALLBACK
33
+ }
34
+ } else {
35
+ HERMES_FALLBACK
36
+ }
37
+
38
+ internal fun Project.needsCodegenFromPackageJson(extension: ReactExtension): Boolean {
39
+ val parsedPackageJson = readPackageJsonFile(this, extension)
40
+ return needsCodegenFromPackageJson(parsedPackageJson)
41
+ }
42
+
43
+ internal fun Project.needsCodegenFromPackageJson(model: ModelPackageJson?): Boolean {
44
+ return model?.codegenConfig != null
45
+ }
46
+ }
@@ -0,0 +1,28 @@
1
+ /*
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ package com.facebook.react.utils
9
+
10
+ internal fun windowsAwareCommandLine(vararg args: Any): List<Any> =
11
+ windowsAwareCommandLine(args.toList())
12
+
13
+ internal fun windowsAwareCommandLine(args: List<Any>): List<Any> =
14
+ if (Os.isWindows()) {
15
+ listOf("cmd", "/c") + args
16
+ } else {
17
+ args
18
+ }
19
+
20
+ internal fun windowsAwareBashCommandLine(
21
+ vararg args: String,
22
+ bashWindowsHome: String? = null
23
+ ): List<String> =
24
+ if (Os.isWindows()) {
25
+ listOf(bashWindowsHome ?: "bash", "-c") + args
26
+ } else {
27
+ args.toList()
28
+ }