/
ReactPlugin.kt
187 lines (164 loc) · 8.54 KB
/
ReactPlugin.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react
import com.android.build.api.variant.AndroidComponentsExtension
import com.android.build.gradle.internal.tasks.factory.dependsOn
import com.facebook.react.internal.PrivateReactExtension
import com.facebook.react.tasks.GenerateCodegenArtifactsTask
import com.facebook.react.tasks.GenerateCodegenSchemaTask
import com.facebook.react.utils.AgpConfiguratorUtils.configureBuildConfigFieldsForApp
import com.facebook.react.utils.AgpConfiguratorUtils.configureBuildConfigFieldsForLibraries
import com.facebook.react.utils.AgpConfiguratorUtils.configureDevPorts
import com.facebook.react.utils.AgpConfiguratorUtils.configureNamespaceForLibraries
import com.facebook.react.utils.BackwardCompatUtils.configureBackwardCompatibilityReactMap
import com.facebook.react.utils.DependencyUtils.configureDependencies
import com.facebook.react.utils.DependencyUtils.configureRepositories
import com.facebook.react.utils.DependencyUtils.readVersionAndGroupStrings
import com.facebook.react.utils.JdkConfiguratorUtils.configureJavaToolChains
import com.facebook.react.utils.JsonUtils
import com.facebook.react.utils.NdkConfiguratorUtils.configureReactNativeNdk
import com.facebook.react.utils.ProjectUtils.needsCodegenFromPackageJson
import com.facebook.react.utils.findPackageJsonFile
import java.io.File
import kotlin.system.exitProcess
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.file.Directory
import org.gradle.api.provider.Provider
import org.gradle.internal.jvm.Jvm
class ReactPlugin : Plugin<Project> {
override fun apply(project: Project) {
checkJvmVersion(project)
val extension = project.extensions.create("react", ReactExtension::class.java, project)
// We register a private extension on the rootProject so that project wide configs
// like codegen config can be propagated from app project to libraries.
val rootExtension =
project.rootProject.extensions.findByType(PrivateReactExtension::class.java)
?: project.rootProject.extensions.create(
"privateReact", PrivateReactExtension::class.java, project)
// App Only Configuration
project.pluginManager.withPlugin("com.android.application") {
// We wire the root extension with the values coming from the app (either user populated or
// defaults).
rootExtension.root.set(extension.root)
rootExtension.reactNativeDir.set(extension.reactNativeDir)
rootExtension.codegenDir.set(extension.codegenDir)
rootExtension.nodeExecutableAndArgs.set(extension.nodeExecutableAndArgs)
project.afterEvaluate {
val reactNativeDir = extension.reactNativeDir.get().asFile
val propertiesFile = File(reactNativeDir, "ReactAndroid/gradle.properties")
val versionAndGroupStrings = readVersionAndGroupStrings(propertiesFile)
val versionString = versionAndGroupStrings.first
val groupString = versionAndGroupStrings.second
configureDependencies(project, versionString, groupString)
configureRepositories(project, reactNativeDir)
}
configureReactNativeNdk(project, extension)
configureBuildConfigFieldsForApp(project, extension)
configureDevPorts(project)
configureBackwardCompatibilityReactMap(project)
configureJavaToolChains(project)
project.extensions.getByType(AndroidComponentsExtension::class.java).apply {
onVariants(selector().all()) { variant ->
project.configureReactTasks(variant = variant, config = extension)
}
}
configureCodegen(project, extension, rootExtension, isLibrary = false)
}
// Library Only Configuration
configureBuildConfigFieldsForLibraries(project)
configureNamespaceForLibraries(project)
project.pluginManager.withPlugin("com.android.library") {
configureCodegen(project, extension, rootExtension, isLibrary = true)
}
}
private fun checkJvmVersion(project: Project) {
val jvmVersion = Jvm.current()?.javaVersion?.majorVersion
if ((jvmVersion?.toIntOrNull() ?: 0) <= 16) {
project.logger.error(
"""
********************************************************************************
ERROR: requires JDK17 or higher.
Incompatible major version detected: '$jvmVersion'
********************************************************************************
"""
.trimIndent())
exitProcess(1)
}
}
/** This function sets up `react-native-codegen` in our Gradle plugin. */
@Suppress("UnstableApiUsage")
private fun configureCodegen(
project: Project,
localExtension: ReactExtension,
rootExtension: PrivateReactExtension,
isLibrary: Boolean
) {
// First, we set up the output dir for the codegen.
val generatedSrcDir: Provider<Directory> =
project.layout.buildDirectory.dir("generated/source/codegen")
// We specify the default value (convention) for jsRootDir.
// It's the root folder for apps (so ../../ from the Gradle project)
// and the package folder for library (so ../ from the Gradle project)
if (isLibrary) {
localExtension.jsRootDir.convention(project.layout.projectDirectory.dir("../"))
} else {
localExtension.jsRootDir.convention(localExtension.root)
}
// We create the task to produce schema from JS files.
val generateCodegenSchemaTask =
project.tasks.register(
"generateCodegenSchemaFromJavaScript", GenerateCodegenSchemaTask::class.java) { it ->
it.nodeExecutableAndArgs.set(rootExtension.nodeExecutableAndArgs)
it.codegenDir.set(rootExtension.codegenDir)
it.generatedSrcDir.set(generatedSrcDir)
// We're reading the package.json at configuration time to properly feed
// the `jsRootDir` @Input property of this task & the onlyIf. Therefore, the
// parsePackageJson should be invoked inside this lambda.
val packageJson = findPackageJsonFile(project, rootExtension.root)
val parsedPackageJson = packageJson?.let { JsonUtils.fromPackageJson(it) }
val jsSrcsDirInPackageJson = parsedPackageJson?.codegenConfig?.jsSrcsDir
if (jsSrcsDirInPackageJson != null) {
it.jsRootDir.set(File(packageJson.parentFile, jsSrcsDirInPackageJson))
} else {
it.jsRootDir.set(localExtension.jsRootDir)
}
val needsCodegenFromPackageJson =
project.needsCodegenFromPackageJson(rootExtension.root)
it.onlyIf { isLibrary || needsCodegenFromPackageJson }
}
// We create the task to generate Java code from schema.
val generateCodegenArtifactsTask =
project.tasks.register(
"generateCodegenArtifactsFromSchema", GenerateCodegenArtifactsTask::class.java) {
it.dependsOn(generateCodegenSchemaTask)
it.reactNativeDir.set(rootExtension.reactNativeDir)
it.nodeExecutableAndArgs.set(rootExtension.nodeExecutableAndArgs)
it.generatedSrcDir.set(generatedSrcDir)
it.packageJsonFile.set(findPackageJsonFile(project, rootExtension.root))
it.codegenJavaPackageName.set(localExtension.codegenJavaPackageName)
it.libraryName.set(localExtension.libraryName)
// Please note that appNeedsCodegen is triggering a read of the package.json at
// configuration time as we need to feed the onlyIf condition of this task.
// Therefore, the appNeedsCodegen needs to be invoked inside this lambda.
val needsCodegenFromPackageJson =
project.needsCodegenFromPackageJson(rootExtension.root)
it.onlyIf { isLibrary || needsCodegenFromPackageJson }
}
// We update the android configuration to include the generated sources.
// This equivalent to this DSL:
//
// android { sourceSets { main { java { srcDirs += "$generatedSrcDir/java" } } } }
project.extensions.getByType(AndroidComponentsExtension::class.java).finalizeDsl { ext ->
ext.sourceSets.getByName("main").java.srcDir(generatedSrcDir.get().dir("java").asFile)
}
// `preBuild` is one of the base tasks automatically registered by AGP.
// This will invoke the codegen before compiling the entire project.
project.tasks.named("preBuild", Task::class.java).dependsOn(generateCodegenArtifactsTask)
}
}