Skip to content

Commit cf914e4

Browse files
cortinicofacebook-github-bot
authored andcommitted
RNGP - Autolinking. Add support for linking projects. (#44799)
Summary: Pull Request resolved: #44799 This is the final part of core autolinking: 1. I split RNGP into an `app-plugin` and a `settings-plugin`. This was necessary as the Gradle modules need to be loaded inside the settings.gradle.kts. 2. I've introduced a Settings Plugin to take care of either invoking the `config` command from CLI or receiving a file in input. 3. I've removed the former `RunAutolinkingConfigTask` as now the command is invoked inside the settings plugin 4. I've added hashing computed based on the lockfiles so we won't be re-executing teh `config` command if the lockfiles are not changed. 5. I've updated RN-Tester to use the core autolinking rather than manual linking for the 2 libraries it's using. Changelog:linking [Internal] [Changed] - RNGP - Autolinking. Add support for linking projects Reviewed By: blakef Differential Revision: D58190363 fbshipit-source-id: 6ab8b36729e77ca715f50a4a00aa0ca4eb5b63b1
1 parent d8a0d30 commit cf914e4

File tree

103 files changed

+983
-389
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

103 files changed

+983
-389
lines changed

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ project.xcworkspace
2424

2525
# Gradle
2626
/build/
27-
/packages/react-native-gradle-plugin/build/
2827
/packages/rn-tester/build
2928
/packages/rn-tester/android/app/.cxx/
3029
/packages/rn-tester/android/app/build/

packages/helloworld/android/app/build.gradle

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ react {
5656
//
5757
// The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
5858
// hermesFlags = ["-O", "-output-source-map"]
59+
60+
/* Autolinking */
61+
autolinkLibrariesWithApp()
5962
}
6063

6164
/**
@@ -121,6 +124,3 @@ dependencies {
121124
implementation jscFlavor
122125
}
123126
}
124-
125-
// TODO: This needs to use the new autolinking code in the gradle-plugin instead
126-
// apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)

packages/helloworld/android/app/src/main/java/com/helloworld/MainApplication.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
package com.helloworld
99

1010
import android.app.Application
11-
import com.facebook.react.PackageList
11+
import com.facebook.react.PackageList2
1212
import com.facebook.react.ReactApplication
1313
import com.facebook.react.ReactHost
1414
import com.facebook.react.ReactNativeHost
@@ -23,7 +23,7 @@ class MainApplication : Application(), ReactApplication {
2323
override val reactNativeHost: ReactNativeHost =
2424
object : DefaultReactNativeHost(this) {
2525
override fun getPackages(): List<ReactPackage> =
26-
PackageList(this).packages.apply {
26+
PackageList2(this).packages.apply {
2727
// Packages that cannot be autolinked yet can be added manually here, for example:
2828
// add(MyReactNativePackage())
2929
}

packages/helloworld/android/settings.gradle

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8+
// Autolinking has now moved into the React Native Gradle Plugin
9+
import com.facebook.react.ReactSettingsExtension
10+
pluginManagement { includeBuild("../node_modules/@react-native/gradle-plugin") }
11+
plugins { id("com.facebook.react.settings") }
12+
extensions.configure(ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() }
13+
814
rootProject.name = 'HelloWorld'
915
include ':app'
10-
// Autolinking has now moved into the React Native Gradle Plugin
1116
includeBuild('../../react-native-gradle-plugin')
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
build/
2+
app-plugin/build/
3+
settings-plugin/build/
4+
shared/build/

packages/react-native-gradle-plugin/build.gradle.kts

Lines changed: 1 addition & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -5,80 +5,7 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8-
import org.gradle.api.internal.classpath.ModuleRegistry
9-
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
10-
import org.gradle.configurationcache.extensions.serviceOf
11-
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
12-
138
plugins {
14-
alias(libs.plugins.kotlin.jvm)
9+
alias(libs.plugins.kotlin.jvm).apply(false)
1510
id("java-gradle-plugin")
1611
}
17-
18-
repositories {
19-
google()
20-
mavenCentral()
21-
}
22-
23-
gradlePlugin {
24-
plugins {
25-
create("react") {
26-
id = "com.facebook.react"
27-
implementationClass = "com.facebook.react.ReactPlugin"
28-
}
29-
create("reactrootproject") {
30-
id = "com.facebook.react.rootproject"
31-
implementationClass = "com.facebook.react.ReactRootProjectPlugin"
32-
}
33-
}
34-
}
35-
36-
group = "com.facebook.react"
37-
38-
dependencies {
39-
implementation(gradleApi())
40-
41-
// The KGP/AGP version is defined by React Native Gradle plugin.
42-
// Therefore we specify an implementation dep rather than a compileOnly.
43-
implementation(libs.kotlin.gradle.plugin)
44-
implementation(libs.android.gradle.plugin)
45-
46-
implementation(libs.gson)
47-
implementation(libs.guava)
48-
implementation(libs.javapoet)
49-
50-
testImplementation(libs.junit)
51-
52-
testRuntimeOnly(
53-
files(
54-
serviceOf<ModuleRegistry>()
55-
.getModule("gradle-tooling-api-builders")
56-
.classpath
57-
.asFiles
58-
.first()))
59-
}
60-
61-
// We intentionally don't build for Java 17 as users will see a cryptic bytecode version
62-
// error first. Instead we produce a Java 11-compatible Gradle Plugin, so that AGP can print their
63-
// nice message showing that JDK 11 (or 17) is required first
64-
java { targetCompatibility = JavaVersion.VERSION_11 }
65-
66-
kotlin { jvmToolchain(17) }
67-
68-
tasks.withType<KotlinCompile>().configureEach {
69-
kotlinOptions {
70-
apiVersion = "1.6"
71-
// See comment above on JDK 11 support
72-
jvmTarget = "11"
73-
allWarningsAsErrors = true
74-
}
75-
}
76-
77-
tasks.withType<Test>().configureEach {
78-
testLogging {
79-
exceptionFormat = TestExceptionFormat.FULL
80-
showExceptions = true
81-
showCauses = true
82-
showStackTraces = true
83-
}
84-
}

packages/react-native-gradle-plugin/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@
2929
"gradle",
3030
"gradlew",
3131
"gradlew.bat",
32-
"src/main",
33-
"README.md"
32+
"README.md",
33+
"react-native-gradle-plugin",
34+
"settings-plugin",
35+
"shared"
3436
]
3537
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# React Native Gradle Plugin
2+
3+
This plugin is used by React Native Apps to configure themselves.
4+
5+
NOTE: It's important that this folder is called `react-native-gradle-plugin` as it's used
6+
by users in their `build.gradle` file as follows:
7+
8+
```gradle
9+
buildscript {
10+
// ...
11+
dependencies {
12+
classpath("com.facebook.react:react-native-gradle-plugin")
13+
}
14+
}
15+
```
16+
17+
The name of the artifact is imposed by the folder name.
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
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+
import org.gradle.api.internal.classpath.ModuleRegistry
9+
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
10+
import org.gradle.configurationcache.extensions.serviceOf
11+
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
12+
13+
plugins {
14+
alias(libs.plugins.kotlin.jvm)
15+
id("java-gradle-plugin")
16+
}
17+
18+
repositories {
19+
google()
20+
mavenCentral()
21+
}
22+
23+
gradlePlugin {
24+
plugins {
25+
create("react") {
26+
id = "com.facebook.react"
27+
implementationClass = "com.facebook.react.ReactPlugin"
28+
}
29+
create("reactrootproject") {
30+
id = "com.facebook.react.rootproject"
31+
implementationClass = "com.facebook.react.ReactRootProjectPlugin"
32+
}
33+
}
34+
}
35+
36+
group = "com.facebook.react"
37+
38+
dependencies {
39+
implementation(project(":shared"))
40+
41+
implementation(gradleApi())
42+
43+
// The KGP/AGP version is defined by React Native Gradle plugin.
44+
// Therefore we specify an implementation dep rather than a compileOnly.
45+
implementation(libs.kotlin.gradle.plugin)
46+
implementation(libs.android.gradle.plugin)
47+
48+
implementation(libs.gson)
49+
implementation(libs.guava)
50+
implementation(libs.javapoet)
51+
52+
testImplementation(libs.junit)
53+
54+
testRuntimeOnly(
55+
files(
56+
serviceOf<ModuleRegistry>()
57+
.getModule("gradle-tooling-api-builders")
58+
.classpath
59+
.asFiles
60+
.first()))
61+
}
62+
63+
// We intentionally don't build for Java 17 as users will see a cryptic bytecode version
64+
// error first. Instead we produce a Java 11-compatible Gradle Plugin, so that AGP can print their
65+
// nice message showing that JDK 11 (or 17) is required first
66+
java { targetCompatibility = JavaVersion.VERSION_11 }
67+
68+
kotlin { jvmToolchain(17) }
69+
70+
tasks.withType<KotlinCompile>().configureEach {
71+
kotlinOptions {
72+
apiVersion = "1.6"
73+
// See comment above on JDK 11 support
74+
jvmTarget = "11"
75+
allWarningsAsErrors = true
76+
}
77+
}
78+
79+
tasks.withType<Test>().configureEach {
80+
testLogging {
81+
exceptionFormat = TestExceptionFormat.FULL
82+
showExceptions = true
83+
showCauses = true
84+
showStackTraces = true
85+
}
86+
}

packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactExtension.kt renamed to packages/react-native-gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactExtension.kt

Lines changed: 50 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,17 @@
77

88
package com.facebook.react
99

10+
import com.facebook.react.utils.JsonUtils
1011
import com.facebook.react.utils.projectPathToLibraryName
12+
import java.io.File
1113
import javax.inject.Inject
1214
import org.gradle.api.Project
1315
import org.gradle.api.file.DirectoryProperty
14-
import org.gradle.api.file.FileCollection
1516
import org.gradle.api.file.RegularFileProperty
1617
import org.gradle.api.provider.ListProperty
1718
import org.gradle.api.provider.Property
1819

19-
abstract class ReactExtension @Inject constructor(project: Project) {
20+
abstract class ReactExtension @Inject constructor(val project: Project) {
2021

2122
private val objects = project.objects
2223

@@ -149,34 +150,54 @@ abstract class ReactExtension @Inject constructor(project: Project) {
149150
val codegenJavaPackageName: Property<String> =
150151
objects.property(String::class.java).convention("com.facebook.fbreact.specs")
151152

152-
/** Auto-linking Config */
153+
/** Auto-linking Utils */
153154

154155
/**
155-
* Location of the JSON file used to configure autolinking. This file is the output of the
156-
* `@react-native-community/cli` config command.
156+
* Utility function to autolink libraries to the app.
157157
*
158-
* If not specified, RNGP will just invoke whatever you pass as [autolinkConfigCommand].
159-
*/
160-
val autolinkConfigFile: RegularFileProperty = objects.fileProperty()
161-
162-
/**
163-
* The command to invoke as source of truth for the autolinking configuration. Default is `["npx",
164-
* "@react-native-community/cli", "config"]`.
165-
*/
166-
val autolinkConfigCommand: ListProperty<String> =
167-
objects
168-
.listProperty(String::class.java)
169-
.convention(listOf("npx", "@react-native-community/cli", "config"))
170-
171-
/**
172-
* Location of the lock files used to consider whether autolinking [autolinkConfigCommand] should
173-
* re-execute or not. If file collection is unchanged, the autolinking command will not be
174-
* re-executed.
175-
*
176-
* If not specified, RNGP will just look for both yarn.lock and package.lock in the [root] folder.
177-
*/
178-
val autolinkLockFiles: Property<FileCollection> =
179-
objects
180-
.property(FileCollection::class.java)
181-
.convention(root.files("../yarn.lock", "../package-lock.json"))
158+
* This function will read the autolinking configuration file and add Gradle dependencies to the
159+
* app. This function should be invoked inside the react {} block in the app's build.gradle and is
160+
* necessary for libraries to be linked correctly.
161+
*/
162+
fun autolinkLibrariesWithApp() {
163+
val inputFile =
164+
project.rootProject.layout.buildDirectory
165+
.file("generated/autolinking/autolinking.json")
166+
.get()
167+
.asFile
168+
val dependenciesToApply = getGradleDependenciesToApply(inputFile)
169+
dependenciesToApply.forEach { (configuration, path) ->
170+
project.dependencies.add(configuration, project.dependencies.project(mapOf("path" to path)))
171+
}
172+
}
173+
174+
companion object {
175+
/**
176+
* Util function to construct a list of Gradle Configuration <-> Project name pairs for
177+
* autolinking. Pairs looks like: "implementation" -> ":react-native_oss-library-example"
178+
*
179+
* They will be applied to the Gradle project for linking the libraries.
180+
*
181+
* @param inputFile The file to read the autolinking configuration from.
182+
* @return A list of Gradle Configuration <-> Project name pairs.
183+
*/
184+
internal fun getGradleDependenciesToApply(inputFile: File): MutableList<Pair<String, String>> {
185+
val model = JsonUtils.fromAutolinkingConfigJson(inputFile)
186+
val result = mutableListOf<Pair<String, String>>()
187+
model?.dependencies?.values?.forEach { deps ->
188+
val nameCleansed = deps.nameCleansed
189+
val dependencyConfiguration = deps.platforms?.android?.dependencyConfiguration
190+
val buildTypes = deps.platforms?.android?.buildTypes ?: emptyList()
191+
if (buildTypes.isEmpty()) {
192+
result.add((dependencyConfiguration ?: "implementation") to ":$nameCleansed")
193+
} else {
194+
buildTypes.forEach { buildType ->
195+
result.add(
196+
(dependencyConfiguration ?: "${buildType}Implementation") to ":$nameCleansed")
197+
}
198+
}
199+
}
200+
return result
201+
}
202+
}
182203
}

0 commit comments

Comments
 (0)