@ohos-ports/react-native-gradle-plugin 0.71.19-beta.0

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 (37) hide show
  1. package/README.md +16 -0
  2. package/build/libs/react-native-gradle-plugin.jar +0 -0
  3. package/build.gradle.kts +59 -0
  4. package/gradle/wrapper/gradle-wrapper.jar +0 -0
  5. package/gradle/wrapper/gradle-wrapper.properties +5 -0
  6. package/gradlew +234 -0
  7. package/gradlew.bat +89 -0
  8. package/index.js +73 -0
  9. package/package.json +30 -0
  10. package/settings.gradle.kts +16 -0
  11. package/src/main/kotlin/com/facebook/react/ReactExtension.kt +151 -0
  12. package/src/main/kotlin/com/facebook/react/ReactPlugin.kt +212 -0
  13. package/src/main/kotlin/com/facebook/react/TaskConfiguration.kt +81 -0
  14. package/src/main/kotlin/com/facebook/react/internal/PrivateReactExtension.kt +56 -0
  15. package/src/main/kotlin/com/facebook/react/model/ModelCodegenConfig.kt +15 -0
  16. package/src/main/kotlin/com/facebook/react/model/ModelCodegenConfigAndroid.kt +10 -0
  17. package/src/main/kotlin/com/facebook/react/model/ModelPackageJson.kt +10 -0
  18. package/src/main/kotlin/com/facebook/react/tasks/BuildCodegenCLITask.kt +58 -0
  19. package/src/main/kotlin/com/facebook/react/tasks/BundleHermesCTask.kt +200 -0
  20. package/src/main/kotlin/com/facebook/react/tasks/GenerateCodegenArtifactsTask.kt +83 -0
  21. package/src/main/kotlin/com/facebook/react/tasks/GenerateCodegenSchemaTask.kt +82 -0
  22. package/src/main/kotlin/com/facebook/react/tasks/internal/PrepareBoostTask.kt +46 -0
  23. package/src/main/kotlin/com/facebook/react/tasks/internal/PrepareGlogTask.kt +79 -0
  24. package/src/main/kotlin/com/facebook/react/tasks/internal/PrepareJSCTask.kt +50 -0
  25. package/src/main/kotlin/com/facebook/react/tasks/internal/PrepareLibeventTask.kt +51 -0
  26. package/src/main/kotlin/com/facebook/react/tasks/internal/PreparePrefabHeadersTask.kt +62 -0
  27. package/src/main/kotlin/com/facebook/react/tasks/internal/utils/PrefabPreprocessingEntry.kt +27 -0
  28. package/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt +54 -0
  29. package/src/main/kotlin/com/facebook/react/utils/BackwardCompatUtils.kt +48 -0
  30. package/src/main/kotlin/com/facebook/react/utils/DependencyUtils.kt +109 -0
  31. package/src/main/kotlin/com/facebook/react/utils/FileUtils.kt +20 -0
  32. package/src/main/kotlin/com/facebook/react/utils/JsonUtils.kt +21 -0
  33. package/src/main/kotlin/com/facebook/react/utils/NdkConfiguratorUtils.kt +147 -0
  34. package/src/main/kotlin/com/facebook/react/utils/Os.kt +44 -0
  35. package/src/main/kotlin/com/facebook/react/utils/PathUtils.kt +231 -0
  36. package/src/main/kotlin/com/facebook/react/utils/ProjectUtils.kt +55 -0
  37. package/src/main/kotlin/com/facebook/react/utils/TaskUtils.kt +28 -0
@@ -0,0 +1,212 @@
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
9
+
10
+ import com.android.build.api.variant.AndroidComponentsExtension
11
+ import com.android.build.gradle.AppExtension
12
+ import com.android.build.gradle.internal.tasks.factory.dependsOn
13
+ import com.facebook.react.internal.PrivateReactExtension
14
+ import com.facebook.react.tasks.BuildCodegenCLITask
15
+ import com.facebook.react.tasks.GenerateCodegenArtifactsTask
16
+ import com.facebook.react.tasks.GenerateCodegenSchemaTask
17
+ import com.facebook.react.utils.AgpConfiguratorUtils.configureBuildConfigFields
18
+ import com.facebook.react.utils.AgpConfiguratorUtils.configureDevPorts
19
+ import com.facebook.react.utils.BackwardCompatUtils.configureBackwardCompatibilityReactMap
20
+ import com.facebook.react.utils.DependencyUtils.configureDependencies
21
+ import com.facebook.react.utils.DependencyUtils.configureRepositories
22
+ import com.facebook.react.utils.DependencyUtils.readVersionAndGroupStrings
23
+ import com.facebook.react.utils.JsonUtils
24
+ import com.facebook.react.utils.NdkConfiguratorUtils.configureReactNativeNdk
25
+ import com.facebook.react.utils.ProjectUtils.needsCodegenFromPackageJson
26
+ import com.facebook.react.utils.findPackageJsonFile
27
+ import java.io.File
28
+ import kotlin.system.exitProcess
29
+ import org.gradle.api.Plugin
30
+ import org.gradle.api.Project
31
+ import org.gradle.api.Task
32
+ import org.gradle.internal.jvm.Jvm
33
+
34
+ class ReactPlugin : Plugin<Project> {
35
+ override fun apply(project: Project) {
36
+ checkJvmVersion(project)
37
+ val extension = project.extensions.create("react", ReactExtension::class.java, project)
38
+
39
+ // We register a private extension on the rootProject so that project wide configs
40
+ // like codegen config can be propagated from app project to libraries.
41
+ val rootExtension =
42
+ project.rootProject.extensions.findByType(PrivateReactExtension::class.java)
43
+ ?: project.rootProject.extensions.create(
44
+ "privateReact", PrivateReactExtension::class.java, project)
45
+
46
+ // App Only Configuration
47
+ project.pluginManager.withPlugin("com.android.application") {
48
+ // We wire the root extension with the values coming from the app (either user populated or
49
+ // defaults).
50
+ rootExtension.root.set(extension.root)
51
+ rootExtension.reactNativeDir.set(extension.reactNativeDir)
52
+ rootExtension.codegenDir.set(extension.codegenDir)
53
+ rootExtension.nodeExecutableAndArgs.set(extension.nodeExecutableAndArgs)
54
+
55
+ project.afterEvaluate {
56
+ val reactNativeDir = extension.reactNativeDir.get().asFile
57
+ val propertiesFile = File(reactNativeDir, "ReactAndroid/gradle.properties")
58
+ val versionAndGroupStrings = readVersionAndGroupStrings(propertiesFile)
59
+ val versionString = versionAndGroupStrings.first
60
+ val groupString = versionAndGroupStrings.second
61
+ configureDependencies(project, versionString, groupString)
62
+ configureRepositories(project, reactNativeDir)
63
+ }
64
+
65
+ configureReactNativeNdk(project, extension)
66
+ configureBuildConfigFields(project)
67
+ configureDevPorts(project)
68
+ configureBackwardCompatibilityReactMap(project)
69
+
70
+ project.extensions.getByType(AndroidComponentsExtension::class.java).apply {
71
+ onVariants(selector().all()) { variant ->
72
+ project.configureReactTasks(variant = variant, config = extension)
73
+ }
74
+ }
75
+
76
+ // This is a legacy AGP api. Needed as AGP 7.3 is not consuming generated resources correctly.
77
+ // Can be removed as we bump to AGP 7.4 stable.
78
+ // This registers the $buildDir/generated/res/react/<variant> folder as a
79
+ // res folder to be consumed with the old AGP Apis which are not broken.
80
+ project.extensions.getByType(AppExtension::class.java).apply {
81
+ this.applicationVariants.all { variant ->
82
+ val isDebuggableVariant =
83
+ extension.debuggableVariants.get().any { it.equals(variant.name, ignoreCase = true) }
84
+ val targetName = variant.name.replaceFirstChar { it.uppercase() }
85
+ val bundleTaskName = "createBundle${targetName}JsAndAssets"
86
+ if (!isDebuggableVariant) {
87
+ variant.registerGeneratedResFolders(
88
+ project.layout.buildDirectory.files("generated/res/react/${variant.name}"))
89
+ variant.mergeResourcesProvider.get().dependsOn(bundleTaskName)
90
+ }
91
+ }
92
+ }
93
+ configureCodegen(project, extension, rootExtension, isLibrary = false)
94
+ }
95
+
96
+ // Library Only Configuration
97
+ project.pluginManager.withPlugin("com.android.library") {
98
+ configureCodegen(project, extension, rootExtension, isLibrary = true)
99
+ }
100
+ }
101
+
102
+ private fun checkJvmVersion(project: Project) {
103
+ val jvmVersion = Jvm.current()?.javaVersion?.majorVersion
104
+ if ((jvmVersion?.toIntOrNull() ?: 0) <= 8) {
105
+ project.logger.error(
106
+ """
107
+
108
+ ********************************************************************************
109
+
110
+ ERROR: requires JDK11 or higher.
111
+ Incompatible major version detected: '$jvmVersion'
112
+
113
+ ********************************************************************************
114
+
115
+ """
116
+ .trimIndent())
117
+ exitProcess(1)
118
+ }
119
+ }
120
+
121
+ /** This function sets up `react-native-codegen` in our Gradle plugin. */
122
+ @Suppress("UnstableApiUsage")
123
+ private fun configureCodegen(
124
+ project: Project,
125
+ localExtension: ReactExtension,
126
+ rootExtension: PrivateReactExtension,
127
+ isLibrary: Boolean
128
+ ) {
129
+ // First, we set up the output dir for the codegen.
130
+ val generatedSrcDir = File(project.buildDir, "generated/source/codegen")
131
+
132
+ // We specify the default value (convention) for jsRootDir.
133
+ // It's the root folder for apps (so ../../ from the Gradle project)
134
+ // and the package folder for library (so ../ from the Gradle project)
135
+ if (isLibrary) {
136
+ localExtension.jsRootDir.convention(project.layout.projectDirectory.dir("../"))
137
+ } else {
138
+ localExtension.jsRootDir.convention(localExtension.root)
139
+ }
140
+
141
+ val buildCodegenTask =
142
+ project.tasks.register("buildCodegenCLI", BuildCodegenCLITask::class.java) {
143
+ it.codegenDir.set(rootExtension.codegenDir)
144
+ val bashWindowsHome = project.findProperty("REACT_WINDOWS_BASH") as String?
145
+ it.bashWindowsHome.set(bashWindowsHome)
146
+
147
+ // Please note that appNeedsCodegen is triggering a read of the package.json at
148
+ // configuration time as we need to feed the onlyIf condition of this task.
149
+ // Therefore, the appNeedsCodegen needs to be invoked inside this lambda.
150
+ val needsCodegenFromPackageJson = project.needsCodegenFromPackageJson(rootExtension.root)
151
+ it.onlyIf { isLibrary || needsCodegenFromPackageJson }
152
+ }
153
+
154
+ // We create the task to produce schema from JS files.
155
+ val generateCodegenSchemaTask =
156
+ project.tasks.register(
157
+ "generateCodegenSchemaFromJavaScript", GenerateCodegenSchemaTask::class.java) { it ->
158
+ it.dependsOn(buildCodegenTask)
159
+ it.nodeExecutableAndArgs.set(rootExtension.nodeExecutableAndArgs)
160
+ it.codegenDir.set(rootExtension.codegenDir)
161
+ it.generatedSrcDir.set(generatedSrcDir)
162
+
163
+ // We're reading the package.json at configuration time to properly feed
164
+ // the `jsRootDir` @Input property of this task & the onlyIf. Therefore, the
165
+ // parsePackageJson should be invoked inside this lambda.
166
+ val packageJson = findPackageJsonFile(project, rootExtension.root)
167
+ val parsedPackageJson = packageJson?.let { JsonUtils.fromCodegenJson(it) }
168
+
169
+ val jsSrcsDirInPackageJson = parsedPackageJson?.codegenConfig?.jsSrcsDir
170
+ if (jsSrcsDirInPackageJson != null) {
171
+ it.jsRootDir.set(File(packageJson.parentFile, jsSrcsDirInPackageJson))
172
+ } else {
173
+ it.jsRootDir.set(localExtension.jsRootDir)
174
+ }
175
+ val needsCodegenFromPackageJson =
176
+ project.needsCodegenFromPackageJson(rootExtension.root)
177
+ it.onlyIf { isLibrary || needsCodegenFromPackageJson }
178
+ }
179
+
180
+ // We create the task to generate Java code from schema.
181
+ val generateCodegenArtifactsTask =
182
+ project.tasks.register(
183
+ "generateCodegenArtifactsFromSchema", GenerateCodegenArtifactsTask::class.java) {
184
+ it.dependsOn(generateCodegenSchemaTask)
185
+ it.reactNativeDir.set(rootExtension.reactNativeDir)
186
+ it.nodeExecutableAndArgs.set(rootExtension.nodeExecutableAndArgs)
187
+ it.generatedSrcDir.set(generatedSrcDir)
188
+ it.packageJsonFile.set(findPackageJsonFile(project, rootExtension.root))
189
+ it.codegenJavaPackageName.set(localExtension.codegenJavaPackageName)
190
+ it.libraryName.set(localExtension.libraryName)
191
+
192
+ // Please note that appNeedsCodegen is triggering a read of the package.json at
193
+ // configuration time as we need to feed the onlyIf condition of this task.
194
+ // Therefore, the appNeedsCodegen needs to be invoked inside this lambda.
195
+ val needsCodegenFromPackageJson =
196
+ project.needsCodegenFromPackageJson(rootExtension.root)
197
+ it.onlyIf { isLibrary || needsCodegenFromPackageJson }
198
+ }
199
+
200
+ // We update the android configuration to include the generated sources.
201
+ // This equivalent to this DSL:
202
+ //
203
+ // android { sourceSets { main { java { srcDirs += "$generatedSrcDir/java" } } } }
204
+ project.extensions.getByType(AndroidComponentsExtension::class.java).finalizeDsl { ext ->
205
+ ext.sourceSets.getByName("main").java.srcDir(File(generatedSrcDir, "java"))
206
+ }
207
+
208
+ // `preBuild` is one of the base tasks automatically registered by AGP.
209
+ // This will invoke the codegen before compiling the entire project.
210
+ project.tasks.named("preBuild", Task::class.java).dependsOn(generateCodegenArtifactsTask)
211
+ }
212
+ }
@@ -0,0 +1,81 @@
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
9
+
10
+ import com.android.build.api.variant.Variant
11
+ import com.facebook.react.tasks.BundleHermesCTask
12
+ import com.facebook.react.utils.NdkConfiguratorUtils.configureJsEnginePackagingOptions
13
+ import com.facebook.react.utils.NdkConfiguratorUtils.configureNewArchPackagingOptions
14
+ import com.facebook.react.utils.ProjectUtils.isHermesEnabled
15
+ import com.facebook.react.utils.detectedCliFile
16
+ import com.facebook.react.utils.detectedEntryFile
17
+ import java.io.File
18
+ import org.gradle.api.Project
19
+
20
+ @Suppress("SpreadOperator", "UnstableApiUsage")
21
+ internal fun Project.configureReactTasks(variant: Variant, config: ReactExtension) {
22
+ val targetName = variant.name.replaceFirstChar { it.uppercase() }
23
+ val targetPath = variant.name
24
+
25
+ // Resources: generated/assets/react/<variant>/index.android.bundle
26
+ val resourcesDir = File(buildDir, "generated/res/react/$targetPath")
27
+ // Bundle: generated/assets/react/<variant>/index.android.bundle
28
+ val jsBundleDir = File(buildDir, "generated/assets/react/$targetPath")
29
+ // Sourcemap: generated/sourcemaps/react/<variant>/index.android.bundle.map
30
+ val jsSourceMapsDir = File(buildDir, "generated/sourcemaps/react/$targetPath")
31
+ // Intermediate packager:
32
+ // intermediates/sourcemaps/react/<variant>/index.android.bundle.packager.map
33
+ // Intermediate compiler:
34
+ // intermediates/sourcemaps/react/<variant>/index.android.bundle.compiler.map
35
+ val jsIntermediateSourceMapsDir = File(buildDir, "intermediates/sourcemaps/react/$targetPath")
36
+
37
+ // The location of the cli.js file for React Native
38
+ val cliFile = detectedCliFile(config)
39
+
40
+ val isHermesEnabledInProject = project.isHermesEnabled
41
+ val isHermesEnabledInThisVariant =
42
+ if (config.enableHermesOnlyInVariants.get().isNotEmpty()) {
43
+ config.enableHermesOnlyInVariants.get().contains(variant.name) && isHermesEnabledInProject
44
+ } else {
45
+ isHermesEnabledInProject
46
+ }
47
+ val isDebuggableVariant =
48
+ config.debuggableVariants.get().any { it.equals(variant.name, ignoreCase = true) }
49
+
50
+ configureNewArchPackagingOptions(project, variant)
51
+ configureJsEnginePackagingOptions(config, variant, isHermesEnabledInThisVariant)
52
+
53
+ if (!isDebuggableVariant) {
54
+ val entryFileEnvVariable = System.getenv("ENTRY_FILE")
55
+ val bundleTask =
56
+ tasks.register("createBundle${targetName}JsAndAssets", BundleHermesCTask::class.java) {
57
+ it.root.set(config.root)
58
+ it.nodeExecutableAndArgs.set(config.nodeExecutableAndArgs)
59
+ it.cliFile.set(cliFile)
60
+ it.bundleCommand.set(config.bundleCommand)
61
+ it.entryFile.set(detectedEntryFile(config, entryFileEnvVariable))
62
+ it.extraPackagerArgs.set(config.extraPackagerArgs)
63
+ it.bundleConfig.set(config.bundleConfig)
64
+ it.bundleAssetName.set(config.bundleAssetName)
65
+ it.jsBundleDir.set(jsBundleDir)
66
+ it.resourcesDir.set(resourcesDir)
67
+ it.hermesEnabled.set(isHermesEnabledInThisVariant)
68
+ it.minifyEnabled.set(!isHermesEnabledInThisVariant)
69
+ it.devEnabled.set(false)
70
+ it.jsIntermediateSourceMapsDir.set(jsIntermediateSourceMapsDir)
71
+ it.jsSourceMapsDir.set(jsSourceMapsDir)
72
+ it.hermesCommand.set(config.hermesCommand)
73
+ it.hermesFlags.set(config.hermesFlags)
74
+ it.reactNativeDir.set(config.reactNativeDir)
75
+ }
76
+ // Currently broken inside AGP 7.3 We need to wait for a release of AGP 7.4 in order to use
77
+ // the addGeneratedSourceDirectory API.
78
+ // variant.sources.res?.addGeneratedSourceDirectory(bundleTask, BundleHermesCTask::resourcesDir)
79
+ variant.sources.assets?.addGeneratedSourceDirectory(bundleTask, BundleHermesCTask::jsBundleDir)
80
+ }
81
+ }
@@ -0,0 +1,56 @@
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.internal
9
+
10
+ import javax.inject.Inject
11
+ import org.gradle.api.Project
12
+ import org.gradle.api.file.DirectoryProperty
13
+ import org.gradle.api.provider.ListProperty
14
+
15
+ /**
16
+ * A private extension we set on the rootProject to make easier to share values at execution time
17
+ * between app project and library project.
18
+ *
19
+ * Specifically, the [codegenDir], [reactNativeDir] and other properties should be provided by apps
20
+ * (for setups like a monorepo which are app specific) and libraries should honor those values.
21
+ *
22
+ * Users are not supposed to access directly this extension from their build.gradle file.
23
+ */
24
+ abstract class PrivateReactExtension @Inject constructor(project: Project) {
25
+
26
+ private val objects = project.objects
27
+
28
+ val root: DirectoryProperty =
29
+ objects
30
+ .directoryProperty()
31
+ .convention(
32
+ // This is the default for the project root if the users hasn't specified anything.
33
+ // If the project is called "react-native-github"
34
+ // - We're inside the Github Repo -> root is defined by RN Tester (so no default
35
+ // needed)
36
+ // - We're inside an includedBuild as we're performing a build from source
37
+ // (then we're inside `node_modules/react-native`, so default should be ../../)
38
+ // If the project is called in any other name
39
+ // - We're inside a user project, so inside the ./android folder. Default should be
40
+ // ../
41
+ // User can always override this default by setting a `root =` inside the template.
42
+ if (project.rootProject.name == "react-native-github") {
43
+ project.rootProject.layout.projectDirectory.dir("../../")
44
+ } else {
45
+ project.rootProject.layout.projectDirectory.dir("../")
46
+ })
47
+
48
+ val reactNativeDir: DirectoryProperty =
49
+ objects.directoryProperty().convention(root.dir("node_modules/react-native"))
50
+
51
+ val nodeExecutableAndArgs: ListProperty<String> =
52
+ objects.listProperty(String::class.java).convention(listOf("node"))
53
+
54
+ val codegenDir: DirectoryProperty =
55
+ objects.directoryProperty().convention(root.dir("node_modules/react-native-codegen"))
56
+ }
@@ -0,0 +1,15 @@
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.model
9
+
10
+ data class ModelCodegenConfig(
11
+ val name: String?,
12
+ val type: String?,
13
+ val jsSrcsDir: String?,
14
+ val android: ModelCodegenConfigAndroid?
15
+ )
@@ -0,0 +1,10 @@
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.model
9
+
10
+ data class ModelCodegenConfigAndroid(val javaPackageName: String?)
@@ -0,0 +1,10 @@
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.model
9
+
10
+ data class ModelPackageJson(val codegenConfig: ModelCodegenConfig?)
@@ -0,0 +1,58 @@
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.tasks
9
+
10
+ import com.facebook.react.utils.Os.unixifyPath
11
+ import com.facebook.react.utils.windowsAwareBashCommandLine
12
+ import org.gradle.api.file.DirectoryProperty
13
+ import org.gradle.api.file.FileCollection
14
+ import org.gradle.api.provider.Property
15
+ import org.gradle.api.tasks.*
16
+
17
+ /**
18
+ * A Task that will call the `scripts/oss/build.sh` script to trigger the creation of the codegen
19
+ * lib artifacts.
20
+ *
21
+ * NOTE: This task is required when using react-native-codegen from source, instead of npm.
22
+ */
23
+ abstract class BuildCodegenCLITask : Exec() {
24
+
25
+ @get:Internal abstract val codegenDir: DirectoryProperty
26
+
27
+ @get:Internal abstract val bashWindowsHome: Property<String>
28
+
29
+ @get:InputFiles
30
+ val input: FileCollection by lazy {
31
+ codegenDir.get().files("scripts", "src", "package.json", ".babelrc", ".prettierrc")
32
+ }
33
+
34
+ @get:OutputDirectories
35
+ val output: FileCollection by lazy { codegenDir.get().files("lib", "node_modules") }
36
+
37
+ init {
38
+ // We need this condition as we want a single instance of BuildCodegenCLITask to execute
39
+ // per project. Therefore we can safely skip the task if the lib/cli/ folder is available.
40
+ onlyIf {
41
+ val cliDir = codegenDir.file("lib/cli/").get().asFile
42
+ !cliDir.exists() || cliDir.listFiles()?.size == 0
43
+ }
44
+ }
45
+
46
+ override fun exec() {
47
+ commandLine(
48
+ windowsAwareBashCommandLine(
49
+ codegenDir.asFile.get().canonicalPath.unixifyPath().plus(BUILD_SCRIPT_PATH),
50
+ bashWindowsHome = bashWindowsHome.orNull,
51
+ ))
52
+ super.exec()
53
+ }
54
+
55
+ companion object {
56
+ private const val BUILD_SCRIPT_PATH = "/scripts/oss/build.sh"
57
+ }
58
+ }
@@ -0,0 +1,200 @@
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.tasks
9
+
10
+ import com.facebook.react.utils.Os.cliPath
11
+ import com.facebook.react.utils.detectOSAwareHermesCommand
12
+ import com.facebook.react.utils.moveTo
13
+ import com.facebook.react.utils.windowsAwareCommandLine
14
+ import java.io.File
15
+ import org.gradle.api.DefaultTask
16
+ import org.gradle.api.file.ConfigurableFileTree
17
+ import org.gradle.api.file.DirectoryProperty
18
+ import org.gradle.api.file.RegularFileProperty
19
+ import org.gradle.api.provider.ListProperty
20
+ import org.gradle.api.provider.Property
21
+ import org.gradle.api.tasks.*
22
+
23
+ abstract class BundleHermesCTask : DefaultTask() {
24
+
25
+ init {
26
+ group = "react"
27
+ }
28
+
29
+ @get:Internal abstract val root: DirectoryProperty
30
+
31
+ @get:InputFiles
32
+ val sources: ConfigurableFileTree =
33
+ project.fileTree(root) {
34
+ it.include("**/*.js")
35
+ it.include("**/*.jsx")
36
+ it.include("**/*.ts")
37
+ it.include("**/*.tsx")
38
+ it.exclude("**/android/**/*")
39
+ it.exclude("**/ios/**/*")
40
+ it.exclude("**/build/**/*")
41
+ it.exclude("**/node_modules/**/*")
42
+ }
43
+
44
+ @get:Input abstract val nodeExecutableAndArgs: ListProperty<String>
45
+
46
+ @get:InputFile abstract val cliFile: RegularFileProperty
47
+
48
+ @get:Internal abstract val reactNativeDir: DirectoryProperty
49
+
50
+ @get:Input abstract val bundleCommand: Property<String>
51
+
52
+ @get:InputFile abstract val entryFile: RegularFileProperty
53
+
54
+ @get:InputFile @get:Optional abstract val bundleConfig: RegularFileProperty
55
+
56
+ @get:Input abstract val bundleAssetName: Property<String>
57
+
58
+ @get:Input abstract val minifyEnabled: Property<Boolean>
59
+
60
+ @get:Input abstract val hermesEnabled: Property<Boolean>
61
+
62
+ @get:Input abstract val devEnabled: Property<Boolean>
63
+
64
+ @get:Input abstract val extraPackagerArgs: ListProperty<String>
65
+
66
+ @get:Input abstract val hermesCommand: Property<String>
67
+
68
+ @get:Input abstract val hermesFlags: ListProperty<String>
69
+
70
+ @get:OutputDirectory abstract val jsBundleDir: DirectoryProperty
71
+
72
+ @get:OutputDirectory abstract val resourcesDir: DirectoryProperty
73
+
74
+ @get:OutputDirectory abstract val jsIntermediateSourceMapsDir: RegularFileProperty
75
+
76
+ @get:OutputDirectory abstract val jsSourceMapsDir: DirectoryProperty
77
+
78
+ @TaskAction
79
+ fun run() {
80
+ jsBundleDir.get().asFile.mkdirs()
81
+ resourcesDir.get().asFile.mkdirs()
82
+ jsIntermediateSourceMapsDir.get().asFile.mkdirs()
83
+ jsSourceMapsDir.get().asFile.mkdirs()
84
+ val bundleAssetFilename = bundleAssetName.get()
85
+
86
+ val bundleFile = File(jsBundleDir.get().asFile, bundleAssetFilename)
87
+ val packagerSourceMap = resolvePackagerSourceMapFile(bundleAssetFilename)
88
+
89
+ val bundleCommand = getBundleCommand(bundleFile, packagerSourceMap)
90
+ runCommand(bundleCommand)
91
+
92
+ if (hermesEnabled.get()) {
93
+ val detectedHermesCommand = detectOSAwareHermesCommand(root.get().asFile, hermesCommand.get())
94
+ val bytecodeFile = File("${bundleFile}.hbc")
95
+ val outputSourceMap = resolveOutputSourceMap(bundleAssetFilename)
96
+ val compilerSourceMap = resolveCompilerSourceMap(bundleAssetFilename)
97
+
98
+ val hermesCommand = getHermescCommand(detectedHermesCommand, bytecodeFile, bundleFile)
99
+ runCommand(hermesCommand)
100
+ bytecodeFile.moveTo(bundleFile)
101
+
102
+ if (hermesFlags.get().contains("-output-source-map")) {
103
+ val hermesTempSourceMapFile = File("$bytecodeFile.map")
104
+ hermesTempSourceMapFile.moveTo(compilerSourceMap)
105
+
106
+ val reactNativeDir = reactNativeDir.get().asFile
107
+ val composeScriptFile = File(reactNativeDir, "scripts/compose-source-maps.js")
108
+ val composeSourceMapsCommand =
109
+ getComposeSourceMapsCommand(
110
+ composeScriptFile, packagerSourceMap, compilerSourceMap, outputSourceMap)
111
+ runCommand(composeSourceMapsCommand)
112
+ }
113
+ }
114
+ }
115
+
116
+ internal fun resolvePackagerSourceMapFile(bundleAssetName: String) =
117
+ if (hermesEnabled.get()) {
118
+ File(jsIntermediateSourceMapsDir.get().asFile, "$bundleAssetName.packager.map")
119
+ } else {
120
+ resolveOutputSourceMap(bundleAssetName)
121
+ }
122
+
123
+ internal fun resolveOutputSourceMap(bundleAssetName: String) =
124
+ File(jsSourceMapsDir.get().asFile, "$bundleAssetName.map")
125
+
126
+ internal fun resolveCompilerSourceMap(bundleAssetName: String) =
127
+ File(jsIntermediateSourceMapsDir.get().asFile, "$bundleAssetName.compiler.map")
128
+
129
+ private fun runCommand(command: List<Any>) {
130
+ val process = ProcessBuilder(command.map { it.toString() })
131
+ .directory(root.get().asFile)
132
+ .redirectErrorStream(true)
133
+ .start()
134
+ val exitCode = process.waitFor()
135
+ if (exitCode != 0) {
136
+ throw org.gradle.api.GradleException("Command failed with exit code $exitCode: $command")
137
+ }
138
+ }
139
+
140
+ internal fun getBundleCommand(bundleFile: File, sourceMapFile: File): List<Any> =
141
+ windowsAwareCommandLine(
142
+ buildList {
143
+ val rootFile = root.get().asFile
144
+ addAll(nodeExecutableAndArgs.get())
145
+ add(cliFile.get().asFile.cliPath(rootFile))
146
+ add(bundleCommand.get())
147
+ add("--platform")
148
+ add("android")
149
+ add("--dev")
150
+ add(devEnabled.get().toString())
151
+ add("--reset-cache")
152
+ add("--entry-file")
153
+ add(entryFile.get().asFile.cliPath(rootFile))
154
+ add("--bundle-output")
155
+ add(bundleFile.cliPath(rootFile))
156
+ add("--assets-dest")
157
+ add(resourcesDir.get().asFile.cliPath(rootFile))
158
+ add("--sourcemap-output")
159
+ add(sourceMapFile.cliPath(rootFile))
160
+ if (bundleConfig.isPresent) {
161
+ add("--config")
162
+ add(bundleConfig.get().asFile.cliPath(rootFile))
163
+ }
164
+ add("--minify")
165
+ add(minifyEnabled.get().toString())
166
+ addAll(extraPackagerArgs.get())
167
+ add("--verbose")
168
+ })
169
+
170
+ internal fun getHermescCommand(
171
+ hermesCommand: String,
172
+ bytecodeFile: File,
173
+ bundleFile: File
174
+ ): List<Any> {
175
+ val rootFile = root.get().asFile
176
+ return windowsAwareCommandLine(
177
+ hermesCommand,
178
+ "-emit-binary",
179
+ "-out",
180
+ bytecodeFile.cliPath(rootFile),
181
+ bundleFile.cliPath(rootFile),
182
+ *hermesFlags.get().toTypedArray())
183
+ }
184
+
185
+ internal fun getComposeSourceMapsCommand(
186
+ composeScript: File,
187
+ packagerSourceMap: File,
188
+ compilerSourceMap: File,
189
+ outputSourceMap: File
190
+ ): List<Any> {
191
+ val rootFile = root.get().asFile
192
+ return windowsAwareCommandLine(
193
+ *nodeExecutableAndArgs.get().toTypedArray(),
194
+ composeScript.cliPath(rootFile),
195
+ packagerSourceMap.cliPath(rootFile),
196
+ compilerSourceMap.cliPath(rootFile),
197
+ "-o",
198
+ outputSourceMap.cliPath(rootFile))
199
+ }
200
+ }