Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,12 @@

package com.facebook.react

import com.android.build.gradle.api.BaseVariant
import com.facebook.react.utils.projectPathToLibraryName
import java.io.File
import javax.inject.Inject
import org.gradle.api.Project
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.MapProperty
import org.gradle.api.provider.Property

abstract class ReactExtension @Inject constructor(project: Project) {
Expand Down Expand Up @@ -54,10 +51,10 @@ abstract class ReactExtension @Inject constructor(project: Project) {
val bundleCommand: Property<String> = objects.property(String::class.java).convention("bundle")

/**
* Custom configuration for the [bundleCommand]. If provided it will be passed over with a
* Custom configuration file for the [bundleCommand]. If provided, it will be passed over with a
* `--config` flag to the bundle command.
*/
val bundleConfig: Property<String> = objects.property(String::class.java)
val bundleConfig: RegularFileProperty = objects.fileProperty()

/**
* The Bundle Asset name. This name will be used also for deriving other bundle outputs such as
Expand All @@ -69,69 +66,24 @@ abstract class ReactExtension @Inject constructor(project: Project) {
objects.property(String::class.java).convention("index.android.bundle")

/**
* Variant Name to File destination map that allows to specify where is the resource dir for a
* specific variant. If a value is supplied, the plugin will copy the bundled resource for that
* variant from `generated/res/react/<variant>` into the custom specified location. Default: {}
* Toggles the .so Cleanup step. If enabled, we will clean up all the unnecessary files before the
* bundle task. If disabled, the developers will have to manually cleanup the files. Default: true
*/
val resourcesDir: MapProperty<String, File> =
objects.mapProperty(String::class.java, File::class.java).convention(emptyMap())

/**
* Variant Name to File destination map that allows to specify where is the asset dir for a
* specific variant. If a value is supplied, the plugin will copy the bundled JS for that variant
* from `generated/assets/react/<variant>` into the custom specified location. Default: {}
*/
val jsBundleDir: MapProperty<String, File> =
objects.mapProperty(String::class.java, File::class.java).convention(emptyMap())

/** ANT-style excludes for the bundle command. Default: ["android / **", "ios / **"] */
val inputExcludes: ListProperty<String> =
objects.listProperty(String::class.java).convention(listOf("android/**", "ios/**"))

/**
* Toggles the VM Cleanup step. If enabled, before the bundle task we will clean up all the
* unnecessary files. If disabled, the developers will have to manually cleanup the files.
* Default: true
*/
val enableVmCleanup: Property<Boolean> = objects.property(Boolean::class.java).convention(true)
val enableSoCleanup: Property<Boolean> = objects.property(Boolean::class.java).convention(true)

/** Extra args that will be passed to the [bundleCommand] Default: [] */
val extraPackagerArgs: ListProperty<String> =
objects.listProperty(String::class.java).convention(emptyList())

/**
* Allows to disable dev mode for certain variants. That's useful if you have a production variant
* (say `canary`) where you don't want dev mode to be enabled. Default: []
* Allows to specify the debuggable variants (by default just 'debug'). Variants in this list
* will:
* - Not be bundled (the bundle file will not be created and won't be copied over).
* - Have the Hermes Debug flags set. That's useful if you have another variant (say `canary`)
* where you want dev mode to be enabled. Default: ['debug']
*/
val devDisabledInVariants: ListProperty<String> =
objects.listProperty(String::class.java).convention(emptyList())

/**
* Functional interface to disable dev mode only on specific [BaseVariant] Default: will check
* [devDisabledInVariants] or return True for Release variants and False for Debug variants.
*/
var disableDevForVariant: (BaseVariant) -> Boolean = { variant ->
variant.name in devDisabledInVariants.get() || variant.isRelease
}

/**
* Variant Name to Boolean map that allows to toggle the bundle command for a specific variant.
* Default: {}
*/
// todo maybe lambda as for hermes?
val bundleIn: MapProperty<String, Boolean> =
objects.mapProperty(String::class.java, Boolean::class.java).convention(emptyMap())

/**
* Functional interface to toggle the bundle command only on specific [BaseVariant] Default: will
* check [bundleIn] or return True for Release variants and False for Debug variants.
*/
var bundleForVariant: (BaseVariant) -> Boolean = { variant ->
if (bundleIn.getting(variant.name).isPresent) bundleIn.getting(variant.name).get()
else if (bundleIn.getting(variant.buildType.name).isPresent)
bundleIn.getting(variant.buildType.name).get()
else variant.isRelease
}
val debuggableVariants: ListProperty<String> =
objects.listProperty(String::class.java).convention(listOf("debug"))

/** Hermes Config */

Expand All @@ -141,35 +93,18 @@ abstract class ReactExtension @Inject constructor(project: Project) {
*/
val hermesCommand: Property<String> = objects.property(String::class.java).convention("")

/** Toggle Hermes for the whole build. Default: false */
val enableHermes: Property<Boolean> = objects.property(Boolean::class.java).convention(false)

/**
* Functional interface to selectively enabled Hermes only on specific [BaseVariant] Default: will
* return [enableHermes] for all the variants.
*/
var enableHermesForVariant: (BaseVariant) -> Boolean = { enableHermes.get() }

/**
* Functional interface specify flags for Hermes on specific [BaseVariant] Default: will return
* [hermesFlagsRelease] for Release variants and [hermesFlagsDebug] for Debug variants.
*/
var hermesFlagsForVariant: (BaseVariant) -> List<String> = { variant ->
if (variant.isRelease) hermesFlagsRelease.get() else hermesFlagsDebug.get()
}

/**
* Functional interface to delete debug files only on specific [BaseVariant] Default: will return
* True for Release variants and False for Debug variants.
* Whether to enable Hermes only on certain variants. If specified as a non-empty list, hermesc
* and the .so cleanup for Hermes will be executed only for variants in this list. An empty list
* assumes you're either using Hermes for all variants or not (see [enableHermes]).
*
* Default: []
*/
var deleteDebugFilesForVariant: (BaseVariant) -> Boolean = { variant -> variant.isRelease }

/** Flags to pass to Hermes for Debug variants. Default: [] */
val hermesFlagsDebug: ListProperty<String> =
val enableHermesOnlyInVariants: ListProperty<String> =
objects.listProperty(String::class.java).convention(emptyList())

/** Flags to pass to Hermes for Release variants. Default: ["-O", "-output-source-map"] */
val hermesFlagsRelease: ListProperty<String> =
/** Flags to pass to Hermesc. Default: ["-O", "-output-source-map"] */
val hermesFlags: ListProperty<String> =
objects.listProperty(String::class.java).convention(listOf("-O", "-output-source-map"))

/**
Expand Down Expand Up @@ -221,4 +156,11 @@ abstract class ReactExtension @Inject constructor(project: Project) {
*/
val codegenJavaPackageName: Property<String> =
objects.property(String::class.java).convention("com.facebook.fbreact.specs")

/**
* Toggles the Codegen for App modules. By default we enable the codegen only for library modules.
* If you want to enable codegen in your app, you will have to set this to true. Default: false
*/
val enableCodegenInApps: Property<Boolean> =
objects.property(Boolean::class.java).convention(false)
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,20 @@
package com.facebook.react

import com.android.build.api.variant.AndroidComponentsExtension
import com.android.build.gradle.AppExtension
import com.android.build.gradle.LibraryExtension
import com.android.build.gradle.internal.tasks.factory.dependsOn
import com.facebook.react.tasks.BuildCodegenCLITask
import com.facebook.react.tasks.GenerateCodegenArtifactsTask
import com.facebook.react.tasks.GenerateCodegenSchemaTask
import com.facebook.react.utils.AgpConfiguratorUtils.configureBuildConfigFields
import com.facebook.react.utils.AgpConfiguratorUtils.configureDevPorts
import com.facebook.react.utils.DependencyUtils.configureDependencies
import com.facebook.react.utils.DependencyUtils.configureRepositories
import com.facebook.react.utils.DependencyUtils.readVersionString
import com.facebook.react.utils.JsonUtils
import com.facebook.react.utils.NdkConfiguratorUtils.configureReactNativeNdk
import com.facebook.react.utils.findPackageJsonFile
import java.io.File
import kotlin.system.exitProcess
import org.gradle.api.Action
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
Expand All @@ -31,8 +31,31 @@ class ReactPlugin : Plugin<Project> {
override fun apply(project: Project) {
checkJvmVersion(project)
val extension = project.extensions.create("react", ReactExtension::class.java, project)
applyAppPlugin(project, extension)
applyCodegenPlugin(project, extension)

// App Only Configuration
project.pluginManager.withPlugin("com.android.application") {
project.afterEvaluate {
val reactNativeDir = extension.reactNativeDir.get().asFile
val propertiesFile = File(reactNativeDir, "ReactAndroid/gradle.properties")
val versionString = readVersionString(propertiesFile)
configureDependencies(project, versionString)
configureRepositories(project, reactNativeDir)
}

configureReactNativeNdk(project, extension)
configureBuildConfigFields(project)
configureDevPorts(project)

project.extensions.getByType(AndroidComponentsExtension::class.java).onVariants { variant ->
project.configureReactTasks(variant = variant, config = extension)
}
configureCodegen(project, extension, isLibrary = false)
}

// Library Only Configuration
project.pluginManager.withPlugin("com.android.library") {
configureCodegen(project, extension, isLibrary = true)
}
}

private fun checkJvmVersion(project: Project) {
Expand All @@ -54,30 +77,12 @@ class ReactPlugin : Plugin<Project> {
}
}

private fun applyAppPlugin(project: Project, config: ReactExtension) {
project.pluginManager.withPlugin("com.android.application") {
configureReactNativeNdk(project, config)
configureBuildConfigFields(project)
configureDevPorts(project)
project.afterEvaluate {
val isAndroidLibrary = project.plugins.hasPlugin("com.android.library")
val variants =
if (isAndroidLibrary) {
project.extensions.getByType(LibraryExtension::class.java).libraryVariants
} else {
project.extensions.getByType(AppExtension::class.java).applicationVariants
}
variants.all { project.configureReactTasks(variant = it, config = config) }
}
}
}

/**
* A plugin to enable react-native-codegen in Gradle environment. See the Gradle API docs for more
* information: https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html
*/
@Suppress("UnstableApiUsage")
private fun applyCodegenPlugin(project: Project, extension: ReactExtension) {
private fun configureCodegen(project: Project, extension: ReactExtension, isLibrary: Boolean) {
// First, we set up the output dir for the codegen.
val generatedSrcDir = File(project.buildDir, "generated/source/codegen")

Expand All @@ -86,6 +91,7 @@ class ReactPlugin : Plugin<Project> {
it.codegenDir.set(extension.codegenDir)
val bashWindowsHome = project.findProperty("REACT_WINDOWS_BASH") as String?
it.bashWindowsHome.set(bashWindowsHome)
it.onlyIf { isLibrary || extension.enableCodegenInApps.get() }
}

// We create the task to produce schema from JS files.
Expand All @@ -96,6 +102,7 @@ class ReactPlugin : Plugin<Project> {
it.nodeExecutableAndArgs.set(extension.nodeExecutableAndArgs)
it.codegenDir.set(extension.codegenDir)
it.generatedSrcDir.set(generatedSrcDir)
it.onlyIf { isLibrary || extension.enableCodegenInApps.get() }

// We're reading the package.json at configuration time to properly feed
// the `jsRootDir` @Input property of this task. Therefore, the
Expand Down Expand Up @@ -123,6 +130,7 @@ class ReactPlugin : Plugin<Project> {
it.packageJsonFile.set(findPackageJsonFile(project, extension))
it.codegenJavaPackageName.set(extension.codegenJavaPackageName)
it.libraryName.set(extension.libraryName)
it.onlyIf { isLibrary || extension.enableCodegenInApps.get() }
}

// We update the android configuration to include the generated sources.
Expand All @@ -133,12 +141,8 @@ class ReactPlugin : Plugin<Project> {
ext.sourceSets.getByName("main").java.srcDir(File(generatedSrcDir, "java"))
}

// `preBuild` is one of the base tasks automatically registered by Gradle.
// `preBuild` is one of the base tasks automatically registered by AGP.
// This will invoke the codegen before compiling the entire project.
val androidPluginHandler = Action { _: Plugin<*> ->
project.tasks.named("preBuild", Task::class.java).dependsOn(generateCodegenArtifactsTask)
}
project.plugins.withId("com.android.application", androidPluginHandler)
project.plugins.withId("com.android.library", androidPluginHandler)
project.tasks.named("preBuild", Task::class.java).dependsOn(generateCodegenArtifactsTask)
}
}
Loading