Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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
8 changes: 8 additions & 0 deletions packages/gradle-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ plugins {

tasks.register("build") {
dependsOn(
":react-native-gradle-plugin-shared:build",
":react-native-library-plugin:build",
":react-native-gradle-plugin:build",
":settings-plugin:build",
":shared-testutil:build",
Expand All @@ -21,6 +23,8 @@ tasks.register("build") {

tasks.register("clean") {
dependsOn(
":react-native-gradle-plugin-shared:clean",
":react-native-library-plugin:clean",
":react-native-gradle-plugin:clean",
":settings-plugin:clean",
":shared-testutil:clean",
Expand All @@ -30,6 +34,8 @@ tasks.register("clean") {

tasks.named("ktfmtCheck") {
dependsOn(
":react-native-gradle-plugin-shared:ktfmtCheck",
":react-native-library-plugin:ktfmtCheck",
":react-native-gradle-plugin:ktfmtCheck",
":settings-plugin:ktfmtCheck",
":shared-testutil:ktfmtCheck",
Expand All @@ -39,6 +45,8 @@ tasks.named("ktfmtCheck") {

tasks.named("ktfmtFormat") {
dependsOn(
":react-native-gradle-plugin-shared:ktfmtFormat",
":react-native-library-plugin:ktfmtFormat",
":react-native-gradle-plugin:ktfmtFormat",
":settings-plugin:ktfmtFormat",
":shared-testutil:ktfmtFormat",
Expand Down
3 changes: 3 additions & 0 deletions packages/gradle-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
"gradlew",
"gradlew.bat",
"README.md",
"react-native-gradle-plugin-shared",
"react-native-library-plugin",
"!react-native-library-plugin/src/test",
"react-native-gradle-plugin",
"!react-native-gradle-plugin/src/test",
"settings-plugin",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* 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.
*/

import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.ktfmt)
}

repositories {
google()
mavenCentral()
}

group = "com.facebook.react"

dependencies {
implementation(project(":shared"))
implementation(gradleApi())
implementation(libs.android.gradle.plugin)
}

java { targetCompatibility = JavaVersion.VERSION_11 }

kotlin { jvmToolchain(17) }

tasks.withType<KotlinCompile>().configureEach {
compilerOptions {
apiVersion.set(KotlinVersion.KOTLIN_2_0)
jvmTarget.set(JvmTarget.JVM_11)
allWarningsAsErrors.set(
project.properties["enableWarningsAsErrors"]?.toString()?.toBoolean() ?: false
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
* 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.ApplicationAndroidComponentsExtension
import com.android.build.api.variant.LibraryAndroidComponentsExtension
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.JsonUtils
import com.facebook.react.utils.findPackageJsonFile
import java.io.File
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.api.tasks.TaskProvider

@Suppress("UnstableApiUsage")
object ReactCodegenConfigurator {
fun configureCodegen(
project: Project,
localExtension: ReactExtension,
rootExtension: PrivateReactExtension,
isLibrary: Boolean,
needsCodegenFromPackageJson: (Project, PrivateReactExtension) -> Boolean,
) {
val generatedSrcDir: Provider<Directory> =
project.layout.buildDirectory.dir("generated/source/codegen")

if (isLibrary) {
localExtension.jsRootDir.convention(project.layout.projectDirectory.dir("../"))
} else {
localExtension.jsRootDir.convention(localExtension.root)
}

val generateCodegenArtifactsTask =
registerCodegenTasks(
project = project,
rootExtension = rootExtension,
generatedSrcDir = generatedSrcDir,
packageJsonFile = { findPackageJsonFile(project, rootExtension.root) },
schemaTaskName = "generateCodegenSchemaFromJavaScript",
artifactsTaskName = "generateCodegenArtifactsFromSchema",
configureJsRoot = { task, packageJson ->
val parsedPackageJson = packageJson?.let { JsonUtils.fromPackageJson(it) }
val jsSrcsDirInPackageJson = parsedPackageJson?.codegenConfig?.jsSrcsDir

if (packageJson != null && jsSrcsDirInPackageJson != null) {
task.jsRootDir.set(File(packageJson.parentFile, jsSrcsDirInPackageJson))
} else {
task.jsRootDir.set(localExtension.jsRootDir)
}
},
configureCodegenArtifacts = { task, _ ->
task.codegenJavaPackageName.set(localExtension.codegenJavaPackageName)
task.libraryName.set(localExtension.libraryName)
},
onlyIf = { packageJson ->
val needsCodegenFromPackageJson =
needsCodegenFromPackageJson(project, rootExtension)
val parsedPackageJson = packageJson?.let { JsonUtils.fromPackageJson(it) }
val includesGeneratedCode =
parsedPackageJson?.codegenConfig?.includesGeneratedCode ?: false
(isLibrary || needsCodegenFromPackageJson) && !includesGeneratedCode
},
)

if (isLibrary) {
project.extensions.getByType(LibraryAndroidComponentsExtension::class.java).finalizeDsl { ext
->
ext.sourceSets
.getByName("main")
.java
.directories
.add(generatedSrcDir.get().dir("java").asFile.path)
}
} else {
project.extensions.getByType(ApplicationAndroidComponentsExtension::class.java).finalizeDsl {
ext ->
ext.sourceSets
.getByName("main")
.java
.directories
.add(generatedSrcDir.get().dir("java").asFile.path)
}
}

project.tasks.named("preBuild", Task::class.java).dependsOn(generateCodegenArtifactsTask)
}

fun registerCodegenTasks(
project: Project,
rootExtension: PrivateReactExtension,
generatedSrcDir: Provider<Directory>,
packageJsonFile: () -> File?,
schemaTaskName: String,
artifactsTaskName: String,
configureJsRoot: (GenerateCodegenSchemaTask, File?) -> Unit,
configureCodegenArtifacts: (GenerateCodegenArtifactsTask, File?) -> Unit,
onlyIf: (File?) -> Boolean = { true },
): TaskProvider<GenerateCodegenArtifactsTask> {
val generateCodegenSchemaTask =
project.tasks.register(
schemaTaskName,
GenerateCodegenSchemaTask::class.java,
) { task ->
val packageJson = packageJsonFile()

task.nodeExecutableAndArgs.set(rootExtension.nodeExecutableAndArgs)
task.codegenDir.set(rootExtension.codegenDir)
task.generatedSrcDir.set(generatedSrcDir)
task.nodeWorkingDir.set(project.layout.projectDirectory.asFile.absolutePath)

configureJsRoot(task, packageJson)

task.jsInputFiles.set(
project.fileTree(task.jsRootDir) { tree ->
tree.include("**/*.js")
tree.include("**/*.jsx")
tree.include("**/*.ts")
tree.include("**/*.tsx")

tree.exclude("node_modules/**/*")
tree.exclude("**/*.d.ts")
tree.exclude("**/build/**/*")
}
)
val shouldRunTask = onlyIf(packageJson)
task.onlyIf { shouldRunTask }
}

return project.tasks.register(
artifactsTaskName,
GenerateCodegenArtifactsTask::class.java,
) { task ->
val packageJson = packageJsonFile()

task.dependsOn(generateCodegenSchemaTask)
task.reactNativeDir.set(rootExtension.reactNativeDir)
task.nodeExecutableAndArgs.set(rootExtension.nodeExecutableAndArgs)
task.generatedSrcDir.set(generatedSrcDir)
task.packageJsonFile.set(packageJson)
task.nodeWorkingDir.set(project.layout.projectDirectory.asFile.absolutePath)

configureCodegenArtifacts(task, packageJson)

val shouldRunTask = onlyIf(packageJson)
task.onlyIf { shouldRunTask }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* 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.facebook.react.internal.PrivateReactExtension
import com.facebook.react.utils.LibraryAgpConfiguratorUtils.configureBuildConfigFieldsForLibraries
import com.facebook.react.utils.LibraryAgpConfiguratorUtils.configureNamespaceForLibraries
import com.facebook.react.utils.ProjectCodegenUtils.needsCodegenFromPackageJson
import org.gradle.api.Project

object ReactLibraryConfigurator {
fun configure(
project: Project,
extension: ReactExtension,
rootExtension: PrivateReactExtension,
) {
configureBuildConfigFieldsForLibraries(project)
configureNamespaceForLibraries(project)
ReactCodegenConfigurator.configureCodegen(
project = project,
localExtension = extension,
rootExtension = rootExtension,
isLibrary = true,
needsCodegenFromPackageJson = { currentProject, privateExtension ->
currentProject.needsCodegenFromPackageJson(privateExtension.root)
},
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* 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.utils

import com.android.build.api.variant.AndroidComponentsExtension
import org.gradle.api.Project

private const val AGP_BUILT_IN_KOTLIN_MAJOR = 9

object KotlinPluginUtils {
fun applyKotlinAndroidPluginIfNeeded(project: Project) {
if (!project.hasBuiltInKotlinSupport()) {
// AGP 9 comes with built-in Kotlin support, so applying `kotlin-android` alongside it causes
// both plugins to register the same `kotlin` extension.
project.applyPluginIfNeeded("kotlin-android")
}
}

/**
* Returns whether the project uses AGP's built-in Kotlin support. It is enabled by default on AGP
* 9 and later, and can be disabled with `android.builtInKotlin=false`.
*/
private fun Project.hasBuiltInKotlinSupport(): Boolean {
val androidComponents = extensions.findByType(AndroidComponentsExtension::class.java)
?: return false
if (androidComponents.pluginVersion.major < AGP_BUILT_IN_KOTLIN_MAJOR) {
return false
}
return findProperty("android.builtInKotlin")?.toString()?.toBoolean() ?: true
}

private fun Project.applyPluginIfNeeded(pluginId: String) {
if (!pluginManager.hasPlugin(pluginId)) {
pluginManager.apply(pluginId)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* 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.utils

import com.android.build.api.variant.LibraryAndroidComponentsExtension
import java.io.File
import javax.xml.parsers.DocumentBuilder
import javax.xml.parsers.DocumentBuilderFactory
import org.gradle.api.Project
import org.w3c.dom.Element

@Suppress("UnstableApiUsage")
object LibraryAgpConfiguratorUtils {
fun configureBuildConfigFieldsForLibraries(project: Project) {
project.extensions.getByType(LibraryAndroidComponentsExtension::class.java).finalizeDsl { ext
->
ext.buildFeatures.buildConfig = true
}
}

fun configureNamespaceForLibraries(project: Project) {
project.extensions.getByType(LibraryAndroidComponentsExtension::class.java).finalizeDsl { ext
->
if (ext.namespace == null) {
val manifestFile =
project.layout.projectDirectory.file("src/main/AndroidManifest.xml").asFile
manifestFile
.takeIf { it.exists() }
?.let { file ->
getPackageNameFromManifest(file)?.let { packageName ->
ext.namespace = packageName
}
}
}
}
}
}

fun getPackageNameFromManifest(manifest: File): String? {
val factory: DocumentBuilderFactory = DocumentBuilderFactory.newInstance()
val builder: DocumentBuilder = factory.newDocumentBuilder()

try {
val xmlDocument = builder.parse(manifest)

val manifestElement = xmlDocument.getElementsByTagName("manifest").item(0) as? Element
val packageName = manifestElement?.getAttribute("package")

return if (packageName.isNullOrEmpty()) null else packageName
} catch (e: Exception) {
return null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ gradlePlugin {
group = "com.facebook.react"

dependencies {
implementation(project(":react-native-gradle-plugin-shared"))
implementation(project(":shared"))

implementation(gradleApi())
Expand Down
Loading
Loading