RNGP - Autolinking. Add support for linking projects. (#44799)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/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
This commit is contained in:
Nicola Corti 2024-06-07 10:32:16 -07:00 committed by Facebook GitHub Bot
parent d8a0d30e70
commit cf914e412d
103 changed files with 980 additions and 386 deletions

1
.gitignore vendored
View File

@ -24,7 +24,6 @@ project.xcworkspace
# Gradle
/build/
/packages/react-native-gradle-plugin/build/
/packages/rn-tester/build
/packages/rn-tester/android/app/.cxx/
/packages/rn-tester/android/app/build/

View File

@ -56,6 +56,9 @@ react {
//
// The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
// hermesFlags = ["-O", "-output-source-map"]
/* Autolinking */
autolinkLibrariesWithApp()
}
/**
@ -121,6 +124,3 @@ dependencies {
implementation jscFlavor
}
}
// TODO: This needs to use the new autolinking code in the gradle-plugin instead
// apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)

View File

@ -8,7 +8,7 @@
package com.helloworld
import android.app.Application
import com.facebook.react.PackageList
import com.facebook.react.PackageList2
import com.facebook.react.ReactApplication
import com.facebook.react.ReactHost
import com.facebook.react.ReactNativeHost
@ -23,7 +23,7 @@ class MainApplication : Application(), ReactApplication {
override val reactNativeHost: ReactNativeHost =
object : DefaultReactNativeHost(this) {
override fun getPackages(): List<ReactPackage> =
PackageList(this).packages.apply {
PackageList2(this).packages.apply {
// Packages that cannot be autolinked yet can be added manually here, for example:
// add(MyReactNativePackage())
}

View File

@ -5,7 +5,12 @@
* LICENSE file in the root directory of this source tree.
*/
// Autolinking has now moved into the React Native Gradle Plugin
import com.facebook.react.ReactSettingsExtension
pluginManagement { includeBuild("../node_modules/@react-native/gradle-plugin") }
plugins { id("com.facebook.react.settings") }
extensions.configure(ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() }
rootProject.name = 'HelloWorld'
include ':app'
// Autolinking has now moved into the React Native Gradle Plugin
includeBuild('../../react-native-gradle-plugin')

View File

@ -0,0 +1,4 @@
build/
app-plugin/build/
settings-plugin/build/
shared/build/

View File

@ -5,80 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import org.gradle.api.internal.classpath.ModuleRegistry
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.configurationcache.extensions.serviceOf
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.kotlin.jvm).apply(false)
id("java-gradle-plugin")
}
repositories {
google()
mavenCentral()
}
gradlePlugin {
plugins {
create("react") {
id = "com.facebook.react"
implementationClass = "com.facebook.react.ReactPlugin"
}
create("reactrootproject") {
id = "com.facebook.react.rootproject"
implementationClass = "com.facebook.react.ReactRootProjectPlugin"
}
}
}
group = "com.facebook.react"
dependencies {
implementation(gradleApi())
// The KGP/AGP version is defined by React Native Gradle plugin.
// Therefore we specify an implementation dep rather than a compileOnly.
implementation(libs.kotlin.gradle.plugin)
implementation(libs.android.gradle.plugin)
implementation(libs.gson)
implementation(libs.guava)
implementation(libs.javapoet)
testImplementation(libs.junit)
testRuntimeOnly(
files(
serviceOf<ModuleRegistry>()
.getModule("gradle-tooling-api-builders")
.classpath
.asFiles
.first()))
}
// We intentionally don't build for Java 17 as users will see a cryptic bytecode version
// error first. Instead we produce a Java 11-compatible Gradle Plugin, so that AGP can print their
// nice message showing that JDK 11 (or 17) is required first
java { targetCompatibility = JavaVersion.VERSION_11 }
kotlin { jvmToolchain(17) }
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions {
apiVersion = "1.6"
// See comment above on JDK 11 support
jvmTarget = "11"
allWarningsAsErrors = true
}
}
tasks.withType<Test>().configureEach {
testLogging {
exceptionFormat = TestExceptionFormat.FULL
showExceptions = true
showCauses = true
showStackTraces = true
}
}

View File

@ -29,7 +29,9 @@
"gradle",
"gradlew",
"gradlew.bat",
"src/main",
"README.md"
"README.md",
"react-native-gradle-plugin",
"settings-plugin",
"shared"
]
}

View File

@ -0,0 +1,17 @@
# React Native Gradle Plugin
This plugin is used by React Native Apps to configure themselves.
NOTE: It's important that this folder is called `react-native-gradle-plugin` as it's used
by users in their `build.gradle` file as follows:
```gradle
buildscript {
// ...
dependencies {
classpath("com.facebook.react:react-native-gradle-plugin")
}
}
```
The name of the artifact is imposed by the folder name.

View File

@ -0,0 +1,86 @@
/*
* 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.gradle.api.internal.classpath.ModuleRegistry
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.configurationcache.extensions.serviceOf
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
alias(libs.plugins.kotlin.jvm)
id("java-gradle-plugin")
}
repositories {
google()
mavenCentral()
}
gradlePlugin {
plugins {
create("react") {
id = "com.facebook.react"
implementationClass = "com.facebook.react.ReactPlugin"
}
create("reactrootproject") {
id = "com.facebook.react.rootproject"
implementationClass = "com.facebook.react.ReactRootProjectPlugin"
}
}
}
group = "com.facebook.react"
dependencies {
implementation(project(":shared"))
implementation(gradleApi())
// The KGP/AGP version is defined by React Native Gradle plugin.
// Therefore we specify an implementation dep rather than a compileOnly.
implementation(libs.kotlin.gradle.plugin)
implementation(libs.android.gradle.plugin)
implementation(libs.gson)
implementation(libs.guava)
implementation(libs.javapoet)
testImplementation(libs.junit)
testRuntimeOnly(
files(
serviceOf<ModuleRegistry>()
.getModule("gradle-tooling-api-builders")
.classpath
.asFiles
.first()))
}
// We intentionally don't build for Java 17 as users will see a cryptic bytecode version
// error first. Instead we produce a Java 11-compatible Gradle Plugin, so that AGP can print their
// nice message showing that JDK 11 (or 17) is required first
java { targetCompatibility = JavaVersion.VERSION_11 }
kotlin { jvmToolchain(17) }
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions {
apiVersion = "1.6"
// See comment above on JDK 11 support
jvmTarget = "11"
allWarningsAsErrors = true
}
}
tasks.withType<Test>().configureEach {
testLogging {
exceptionFormat = TestExceptionFormat.FULL
showExceptions = true
showCauses = true
showStackTraces = true
}
}

View File

@ -7,16 +7,17 @@
package com.facebook.react
import com.facebook.react.utils.JsonUtils
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.FileCollection
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property
abstract class ReactExtension @Inject constructor(project: Project) {
abstract class ReactExtension @Inject constructor(val project: Project) {
private val objects = project.objects
@ -149,34 +150,54 @@ abstract class ReactExtension @Inject constructor(project: Project) {
val codegenJavaPackageName: Property<String> =
objects.property(String::class.java).convention("com.facebook.fbreact.specs")
/** Auto-linking Config */
/** Auto-linking Utils */
/**
* Location of the JSON file used to configure autolinking. This file is the output of the
* `@react-native-community/cli` config command.
* Utility function to autolink libraries to the app.
*
* If not specified, RNGP will just invoke whatever you pass as [autolinkConfigCommand].
* This function will read the autolinking configuration file and add Gradle dependencies to the
* app. This function should be invoked inside the react {} block in the app's build.gradle and is
* necessary for libraries to be linked correctly.
*/
val autolinkConfigFile: RegularFileProperty = objects.fileProperty()
fun autolinkLibrariesWithApp() {
val inputFile =
project.rootProject.layout.buildDirectory
.file("generated/autolinking/autolinking.json")
.get()
.asFile
val dependenciesToApply = getGradleDependenciesToApply(inputFile)
dependenciesToApply.forEach { (configuration, path) ->
project.dependencies.add(configuration, project.dependencies.project(mapOf("path" to path)))
}
}
/**
* The command to invoke as source of truth for the autolinking configuration. Default is `["npx",
* "@react-native-community/cli", "config"]`.
*/
val autolinkConfigCommand: ListProperty<String> =
objects
.listProperty(String::class.java)
.convention(listOf("npx", "@react-native-community/cli", "config"))
/**
* Location of the lock files used to consider whether autolinking [autolinkConfigCommand] should
* re-execute or not. If file collection is unchanged, the autolinking command will not be
* re-executed.
*
* If not specified, RNGP will just look for both yarn.lock and package.lock in the [root] folder.
*/
val autolinkLockFiles: Property<FileCollection> =
objects
.property(FileCollection::class.java)
.convention(root.files("../yarn.lock", "../package-lock.json"))
companion object {
/**
* Util function to construct a list of Gradle Configuration <-> Project name pairs for
* autolinking. Pairs looks like: "implementation" -> ":react-native_oss-library-example"
*
* They will be applied to the Gradle project for linking the libraries.
*
* @param inputFile The file to read the autolinking configuration from.
* @return A list of Gradle Configuration <-> Project name pairs.
*/
internal fun getGradleDependenciesToApply(inputFile: File): MutableList<Pair<String, String>> {
val model = JsonUtils.fromAutolinkingConfigJson(inputFile)
val result = mutableListOf<Pair<String, String>>()
model?.dependencies?.values?.forEach { deps ->
val nameCleansed = deps.nameCleansed
val dependencyConfiguration = deps.platforms?.android?.dependencyConfiguration
val buildTypes = deps.platforms?.android?.buildTypes ?: emptyList()
if (buildTypes.isEmpty()) {
result.add((dependencyConfiguration ?: "implementation") to ":$nameCleansed")
} else {
buildTypes.forEach { buildType ->
result.add(
(dependencyConfiguration ?: "${buildType}Implementation") to ":$nameCleansed")
}
}
}
return result
}
}
}

View File

@ -14,7 +14,6 @@ import com.facebook.react.tasks.GenerateAutolinkingNewArchitecturesFileTask
import com.facebook.react.tasks.GenerateCodegenArtifactsTask
import com.facebook.react.tasks.GenerateCodegenSchemaTask
import com.facebook.react.tasks.GeneratePackageListTask
import com.facebook.react.tasks.RunAutolinkingConfigTask
import com.facebook.react.utils.AgpConfiguratorUtils.configureBuildConfigFieldsForApp
import com.facebook.react.utils.AgpConfiguratorUtils.configureBuildConfigFieldsForLibraries
import com.facebook.react.utils.AgpConfiguratorUtils.configureDevPorts
@ -220,22 +219,15 @@ class ReactPlugin : Plugin<Project> {
project: Project,
extension: ReactExtension,
) {
val generatedAutolinkingDir: Provider<Directory> =
project.layout.buildDirectory.dir("generated/autolinking")
val generatedAutolinkingJavaDir: Provider<Directory> =
project.layout.buildDirectory.dir("generated/autolinking/src/main/java")
val generatedAutolinkingJniDir: Provider<Directory> =
project.layout.buildDirectory.dir("generated/autolinking/src/main/jni")
val configOutputFile = generatedAutolinkingDir.get().file("config-output.json")
val runAutolinkingConfigTask =
project.tasks.register("runAutolinkingConfig", RunAutolinkingConfigTask::class.java) { task
->
task.autolinkConfigCommand.set(extension.autolinkConfigCommand)
task.autolinkConfigFile.set(extension.autolinkConfigFile)
task.autolinkOutputFile.set(configOutputFile)
task.autolinkLockFiles.set(extension.autolinkLockFiles)
}
// The autolinking.json file is available in the root build folder as it's generated
// by ReactSettingsPlugin.kt
val rootGeneratedAutolinkingFile =
project.rootProject.layout.buildDirectory.file("generated/autolinking/autolinking.json")
// We add a task called generateAutolinkingPackageList to do not clash with the existing task
// called generatePackageList. This can to be renamed once we unlink the rn <-> cli
@ -243,8 +235,7 @@ class ReactPlugin : Plugin<Project> {
val generatePackageListTask =
project.tasks.register(
"generateAutolinkingPackageList", GeneratePackageListTask::class.java) { task ->
task.dependsOn(runAutolinkingConfigTask)
task.autolinkInputFile.set(configOutputFile)
task.autolinkInputFile.set(rootGeneratedAutolinkingFile)
task.generatedOutputDirectory.set(generatedAutolinkingJavaDir)
}
@ -254,8 +245,7 @@ class ReactPlugin : Plugin<Project> {
project.tasks.register(
"generateAutolinkingNewArchitectureFiles",
GenerateAutolinkingNewArchitecturesFileTask::class.java) { task ->
task.dependsOn(runAutolinkingConfigTask)
task.autolinkInputFile.set(configOutputFile)
task.autolinkInputFile.set(rootGeneratedAutolinkingFile)
task.generatedOutputDirectory.set(generatedAutolinkingJniDir)
}
project.tasks

View File

@ -54,17 +54,20 @@ abstract class GenerateAutolinkingNewArchitecturesFileTask : DefaultTask() {
val libraryIncludes =
packages.joinToString("\n") { dep ->
var addDirectoryString = ""
if (dep.libraryName != null && dep.cmakeListsPath != null) {
val libraryName = dep.libraryName
val cmakeListsPath = dep.cmakeListsPath
val cxxModuleCMakeListsPath = dep.cxxModuleCMakeListsPath
if (libraryName != null && cmakeListsPath != null) {
// If user provided a custom cmakeListsPath, let's honor it.
val nativeFolderPath = dep.cmakeListsPath.replace("CMakeLists.txt", "")
val nativeFolderPath = cmakeListsPath.replace("CMakeLists.txt", "")
addDirectoryString +=
"add_subdirectory($nativeFolderPath ${dep.libraryName}_autolinked_build)"
"add_subdirectory($nativeFolderPath ${libraryName}_autolinked_build)"
}
if (dep.cxxModuleCMakeListsPath != null) {
if (cxxModuleCMakeListsPath != null) {
// If user provided a custom cxxModuleCMakeListsPath, let's honor it.
val nativeFolderPath = dep.cxxModuleCMakeListsPath.replace("CMakeLists.txt", "")
val nativeFolderPath = cxxModuleCMakeListsPath.replace("CMakeLists.txt", "")
addDirectoryString +=
"\nadd_subdirectory($nativeFolderPath ${dep.libraryName}_cxxmodule_autolinked_build)"
"\nadd_subdirectory($nativeFolderPath ${libraryName}_cxxmodule_autolinked_build)"
}
addDirectoryString
}

View File

@ -0,0 +1,164 @@
/*
* 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.ReactExtension.Companion.getGradleDependenciesToApply
import org.intellij.lang.annotations.Language
import org.junit.Assert.*
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
class ReactExtensionTest {
@get:Rule val tempFolder = TemporaryFolder()
@Test
fun getGradleDependenciesToApply_withEmptyFile_returnsEmptyMap() {
val validJsonFile =
createJsonFile(
"""
{
"reactNativeVersion": "1000.0.0"
}
"""
.trimIndent())
val deps = getGradleDependenciesToApply(validJsonFile)
assertEquals(0, deps.size)
}
@Test
fun getGradleDependenciesToApply_withOneDependency_returnsValidDep() {
val validJsonFile =
createJsonFile(
"""
{
"reactNativeVersion": "1000.0.0",
"dependencies": {
"@react-native/oss-library-example": {
"root": "./node_modules/@react-native/oss-library-example",
"name": "@react-native/oss-library-example",
"platforms": {
"android": {
"sourceDir": "src/main/java",
"packageImportPath": "com.facebook.react"
}
}
}
}
}
"""
.trimIndent())
val deps = getGradleDependenciesToApply(validJsonFile)
assertEquals(1, deps.size)
assertTrue("implementation" to ":react-native_oss-library-example" in deps)
}
@Test
fun getGradleDependenciesToApply_withDependencyConfiguration_returnsValidConfiguration() {
val validJsonFile =
createJsonFile(
"""
{
"reactNativeVersion": "1000.0.0",
"dependencies": {
"@react-native/oss-library-example": {
"root": "./node_modules/@react-native/oss-library-example",
"name": "@react-native/oss-library-example",
"platforms": {
"android": {
"sourceDir": "src/main/java",
"packageImportPath": "com.facebook.react",
"dependencyConfiguration": "compileOnly"
}
}
}
}
}
"""
.trimIndent())
val deps = getGradleDependenciesToApply(validJsonFile)
assertEquals(1, deps.size)
assertTrue("compileOnly" to ":react-native_oss-library-example" in deps)
}
@Test
fun getGradleDependenciesToApply_withBuildTypes_returnsValidConfiguration() {
val validJsonFile =
createJsonFile(
"""
{
"reactNativeVersion": "1000.0.0",
"dependencies": {
"@react-native/oss-library-example": {
"root": "./node_modules/@react-native/oss-library-example",
"name": "@react-native/oss-library-example",
"platforms": {
"android": {
"sourceDir": "src/main/java",
"packageImportPath": "com.facebook.react",
"buildTypes": ["debug", "release"]
}
}
}
}
}
"""
.trimIndent())
val deps = getGradleDependenciesToApply(validJsonFile)
assertEquals(2, deps.size)
assertTrue("debugImplementation" to ":react-native_oss-library-example" in deps)
assertTrue("releaseImplementation" to ":react-native_oss-library-example" in deps)
}
@Test
fun getGradleDependenciesToApply_withMultipleDependencies_returnsValidConfiguration() {
val validJsonFile =
createJsonFile(
"""
{
"reactNativeVersion": "1000.0.0",
"dependencies": {
"@react-native/oss-library-example": {
"root": "./node_modules/@react-native/oss-library-example",
"name": "@react-native/oss-library-example",
"platforms": {
"android": {
"sourceDir": "src/main/java",
"packageImportPath": "com.facebook.react"
}
}
},
"@react-native/another-library-for-testing": {
"root": "./node_modules/@react-native/another-library-for-testing",
"name": "@react-native/another-library-for-testing",
"platforms": {
"android": {
"sourceDir": "src/main/java",
"packageImportPath": "com.facebook.react"
}
}
}
}
}
"""
.trimIndent())
val deps = getGradleDependenciesToApply(validJsonFile)
assertEquals(2, deps.size)
assertTrue("implementation" to ":react-native_oss-library-example" in deps)
assertTrue("implementation" to ":react-native_another-library-for-testing" in deps)
}
private fun createJsonFile(@Language("JSON") input: String) =
tempFolder.newFile().apply { writeText(input) }
}

View File

@ -0,0 +1,76 @@
/*
* 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.gradle.api.internal.classpath.ModuleRegistry
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.configurationcache.extensions.serviceOf
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
alias(libs.plugins.kotlin.jvm)
id("java-gradle-plugin")
}
repositories {
google()
mavenCentral()
}
gradlePlugin {
plugins {
create("react.settings") {
id = "com.facebook.react.settings"
implementationClass = "com.facebook.react.ReactSettingsPlugin"
}
}
}
group = "com.facebook.react"
dependencies {
implementation(project(":shared"))
implementation(gradleApi())
implementation(libs.gson)
implementation(libs.guava)
implementation(libs.javapoet)
testImplementation(libs.junit)
testRuntimeOnly(
files(
serviceOf<ModuleRegistry>()
.getModule("gradle-tooling-api-builders")
.classpath
.asFiles
.first()))
}
// We intentionally don't build for Java 17 as users will see a cryptic bytecode version
// error first. Instead we produce a Java 11-compatible Gradle Plugin, so that AGP can print their
// nice message showing that JDK 11 (or 17) is required first
java { targetCompatibility = JavaVersion.VERSION_11 }
kotlin { jvmToolchain(17) }
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions {
apiVersion = "1.6"
// See comment above on JDK 11 support
jvmTarget = "11"
allWarningsAsErrors = true
}
}
tasks.withType<Test>().configureEach {
testLogging {
exceptionFormat = TestExceptionFormat.FULL
showExceptions = true
showCauses = true
showStackTraces = true
}
}

View File

@ -0,0 +1,121 @@
/*
* 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.utils.JsonUtils
import java.io.File
import java.math.BigInteger
import java.security.MessageDigest
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import org.gradle.api.file.FileCollection
import org.gradle.api.initialization.Settings
abstract class ReactSettingsExtension @Inject constructor(val settings: Settings) {
private val outputFile =
settings.layout.rootDirectory.file("build/generated/autolinking/autolinking.json").asFile
private val outputFolder =
settings.layout.rootDirectory.file("build/generated/autolinking/").asFile
/**
* Utility function to autolink libraries using an external command as source of truth.
*
* This should be invoked inside the `settings.gradle` file, and will make sure the Gradle project
* is loading all the discovered libraries.
*
* @param command The command to execute to get the autolinking configuration. Default is
* `npx @react-native-community/cli config`.
* @param workingDirectory The directory where the command should be executed.
* @param lockFiles The list of lock files to check for changes (if lockfiles are not changed, the
* command will not be executed).
*/
@JvmOverloads
public fun autolinkLibrariesFromCommand(
command: List<String> = listOf("npx", "@react-native-community/cli", "config"),
workingDirectory: File? = settings.layout.rootDirectory.dir("../").asFile,
lockFiles: FileCollection =
settings.layout.rootDirectory.dir("../").files("yarn.lock", "package-lock.json")
) {
outputFile.parentFile.mkdirs()
val lockFilesChanged = checkAndUpdateLockfiles(lockFiles, outputFolder)
if (lockFilesChanged || outputFile.exists().not()) {
ProcessBuilder(command)
.directory(workingDirectory)
.redirectOutput(ProcessBuilder.Redirect.to(outputFile))
.redirectError(ProcessBuilder.Redirect.INHERIT)
.start()
.waitFor(5, TimeUnit.MINUTES)
}
linkLibraries(getLibrariesToAutolink(outputFile))
}
/**
* Utility function to autolink libraries using an external file as source of truth.
*
* The file should be a JSON file with the same structure as the one generated by the
* `npx @react-native-community/cli config` command.
*
* @param autolinkConfigFile The file to read the autolinking configuration from.
*/
public fun autolinkLibrariesFromConfigFile(
autolinkConfigFile: File,
) {
// We copy the file to the build directory so that the various Gradle tasks can access it.
autolinkConfigFile.copyTo(outputFile, overwrite = true)
linkLibraries(getLibrariesToAutolink(autolinkConfigFile))
}
/**
* Utility function so that for each tuple :project-name -> project-dir, it instructs Gradle to
* lad this extra module.
*/
private fun linkLibraries(input: Map<String, File>) {
input.forEach { (path, projectDir) ->
settings.include(path)
settings.project(path).projectDir = projectDir
}
}
companion object {
private val md = MessageDigest.getInstance("SHA-256")
/**
* Utility function to check if the provided lockfiles have been updated or not. This function
* will both check and update the lockfiles hashes if necessary.
*
* @param lockFiles The [FileCollection] of the lockfiles to check.
* @param outputFolder The folder where the hashes will be stored.
* @return `true` if the lockfiles have been updated, `false` otherwise.
*/
internal fun checkAndUpdateLockfiles(lockFiles: FileCollection, outputFolder: File): Boolean {
var changed = false
lockFiles.forEach { lockFile ->
if (lockFile.exists()) {
val sha = computeSha256(lockFile)
val shaFile = File(outputFolder, "${lockFile.name}.sha")
if (shaFile.exists().not() || shaFile.readText() != sha) {
shaFile.writeText(sha)
changed = true
}
}
}
return changed
}
internal fun getLibrariesToAutolink(buildFile: File): Map<String, File> {
val model = JsonUtils.fromAutolinkingConfigJson(buildFile)
return model?.dependencies?.values?.associate { deps ->
":${deps.nameCleansed}" to File(deps.platforms?.android?.sourceDir)
} ?: emptyMap()
}
internal fun computeSha256(lockFile: File) =
String.format("%032x", BigInteger(1, md.digest(lockFile.readBytes())))
}
}

View File

@ -0,0 +1,23 @@
/*
* 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 org.gradle.api.Plugin
import org.gradle.api.initialization.Settings
/**
* This is the settings.gradle plugin for React Native.
*
* It just registers the [ReactSettingsExtension] extension, so that utility functions over there
* can be called to support autolinking.
*/
class ReactSettingsPlugin : Plugin<Settings> {
override fun apply(settings: Settings) {
settings.extensions.create("reactSettings", ReactSettingsExtension::class.java, settings)
}
}

View File

@ -0,0 +1,211 @@
/*
* 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.ReactSettingsExtension.Companion.checkAndUpdateLockfiles
import com.facebook.react.ReactSettingsExtension.Companion.computeSha256
import com.facebook.react.ReactSettingsExtension.Companion.getLibrariesToAutolink
import groovy.test.GroovyTestCase.assertEquals
import java.io.File
import org.gradle.testfixtures.ProjectBuilder
import org.intellij.lang.annotations.Language
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
class ReactSettingsExtensionTest {
@get:Rule val tempFolder = TemporaryFolder()
@Test
fun computeSha256_worksCorrectly() {
val validFile =
createJsonFile(
"""
{
"value": "¯\\_(ツ)_/¯"
}
"""
.trimIndent())
assertEquals(
"838aa9a72a16fdd55b0d49b510a82e264a30f59333b5fdd97c7798a29146f6a8",
computeSha256(validFile))
}
@Test
fun getLibrariesToAutolink_withEmptyFile_returnsEmptyMap() {
val validJsonFile =
createJsonFile(
"""
{
"reactNativeVersion": "1000.0.0"
}
"""
.trimIndent())
val map = getLibrariesToAutolink(validJsonFile)
assertEquals(0, map.keys.size)
}
@Test
fun getLibrariesToAutolink_withLibraryToAutolink_returnsValidMap() {
val validJsonFile =
createJsonFile(
"""
{
"reactNativeVersion": "1000.0.0",
"dependencies": {
"@react-native/oss-library-example": {
"root": "./node_modules/@react-native/oss-library-example",
"name": "@react-native/oss-library-example",
"platforms": {
"ios": {
"podspecPath": "./node_modules/@react-native/oss-library-example/OSSLibraryExample.podspec",
"version": "0.0.1",
"configurations": [],
"scriptPhases": []
},
"android": {
"sourceDir": "./node_modules/@react-native/oss-library-example/android",
"packageImportPath": "import com.facebook.react.osslibraryexample.OSSLibraryExamplePackage;",
"packageInstance": "new OSSLibraryExamplePackage()",
"buildTypes": ["staging", "debug", "release"],
"libraryName": "OSSLibraryExampleSpec",
"componentDescriptors": [
"SampleNativeComponentComponentDescriptor"
],
"cmakeListsPath": "./node_modules/@react-native/oss-library-example/android/build/generated/source/codegen/jni/CMakeLists.txt",
"cxxModuleCMakeListsModuleName": null,
"cxxModuleCMakeListsPath": null,
"cxxModuleHeaderName": null,
"dependencyConfiguration": "implementation"
}
}
}
}
}
"""
.trimIndent())
val map = getLibrariesToAutolink(validJsonFile)
assertEquals(1, map.keys.size)
assertTrue(":react-native_oss-library-example" in map.keys)
assertEquals(
File("./node_modules/@react-native/oss-library-example/android"),
map[":react-native_oss-library-example"])
}
@Test
fun checkAndUpdateLockfiles_withNothingToCheck_returnsFalse() {
val project = ProjectBuilder.builder().build()
val noFiles = project.files()
assertFalse(checkAndUpdateLockfiles(noFiles, tempFolder.root))
}
@Test
fun checkAndUpdateLockfiles_withOneLockfileNoHash_returnsTrue() {
val project = ProjectBuilder.builder().withProjectDir(tempFolder.root).build()
val buildFolder = tempFolder.newFolder("build")
tempFolder.newFile("yarn.lock").apply { writeText("I'm a lockfile") }
val lockfileCollection = project.files("yarn.lock")
assertTrue(checkAndUpdateLockfiles(lockfileCollection, buildFolder))
assertTrue(File(buildFolder, "yarn.lock.sha").exists())
assertEquals(
"76046b72442ee7eb130627e56c3db7c9907eef4913b17ad130335edc0eb702a8",
File(buildFolder, "yarn.lock.sha").readText())
}
@Test
fun checkAndUpdateLockfiles_withOneLockfileInvalidHash_returnsTrue() {
val project = ProjectBuilder.builder().withProjectDir(tempFolder.root).build()
val buildFolder =
tempFolder.newFolder("build").apply {
File(this, "yarn.lock.sha").writeText("Just a stale hash")
}
tempFolder.newFile("yarn.lock").apply { writeText("I'm a lockfile") }
val lockfileCollection = project.files("yarn.lock")
assertTrue(checkAndUpdateLockfiles(lockfileCollection, buildFolder))
assertTrue(File(buildFolder, "yarn.lock.sha").exists())
assertEquals(
"76046b72442ee7eb130627e56c3db7c9907eef4913b17ad130335edc0eb702a8",
File(buildFolder, "yarn.lock.sha").readText())
}
@Test
fun checkAndUpdateLockfiles_withOneLockfileValidHash_returnsFalse() {
val project = ProjectBuilder.builder().withProjectDir(tempFolder.root).build()
val buildFolder =
tempFolder.newFolder("build").apply {
File(this, "yarn.lock.sha")
.writeText("76046b72442ee7eb130627e56c3db7c9907eef4913b17ad130335edc0eb702a8")
}
tempFolder.newFile("yarn.lock").apply { writeText("I'm a lockfile") }
val lockfileCollection = project.files("yarn.lock")
assertFalse(checkAndUpdateLockfiles(lockfileCollection, buildFolder))
assertTrue(File(buildFolder, "yarn.lock.sha").exists())
assertEquals(
"76046b72442ee7eb130627e56c3db7c9907eef4913b17ad130335edc0eb702a8",
File(buildFolder, "yarn.lock.sha").readText())
}
@Test
fun checkAndUpdateLockfiles_withMultipleLockfilesInvalidHash_returnsTrue() {
val project = ProjectBuilder.builder().withProjectDir(tempFolder.root).build()
val buildFolder =
tempFolder.newFolder("build").apply {
File(this, "yarn.lock.sha").writeText("I'm an invalid hash")
}
tempFolder.newFile("yarn.lock").apply { writeText("I'm a lockfile") }
tempFolder.newFile("package-lock.json").apply { writeText("and I'm another lockfile") }
val lockfileCollection = project.files("yarn.lock", "package-lock.json")
assertTrue(checkAndUpdateLockfiles(lockfileCollection, buildFolder))
assertTrue(File(buildFolder, "yarn.lock.sha").exists())
assertTrue(File(buildFolder, "package-lock.json.sha").exists())
assertEquals(
"76046b72442ee7eb130627e56c3db7c9907eef4913b17ad130335edc0eb702a8",
File(buildFolder, "yarn.lock.sha").readText())
assertEquals(
"9be5bca432b81becf4f54451aea021add68376330581eaa93ab9a0b3e4e29a3b",
File(buildFolder, "package-lock.json.sha").readText())
}
@Test
fun checkAndUpdateLockfiles_withMultipleLockfilesValidHash_returnsFalse() {
val project = ProjectBuilder.builder().withProjectDir(tempFolder.root).build()
val buildFolder =
tempFolder.newFolder("build").apply {
File(this, "yarn.lock.sha")
.writeText("76046b72442ee7eb130627e56c3db7c9907eef4913b17ad130335edc0eb702a8")
File(this, "package-lock.json.sha")
.writeText("9be5bca432b81becf4f54451aea021add68376330581eaa93ab9a0b3e4e29a3b")
}
tempFolder.newFile("yarn.lock").apply { writeText("I'm a lockfile") }
tempFolder.newFile("package-lock.json").apply { writeText("and I'm another lockfile") }
val lockfileCollection = project.files("yarn.lock", "package-lock.json")
assertFalse(checkAndUpdateLockfiles(lockfileCollection, buildFolder))
assertTrue(File(buildFolder, "yarn.lock.sha").exists())
assertTrue(File(buildFolder, "package-lock.json.sha").exists())
assertEquals(
"76046b72442ee7eb130627e56c3db7c9907eef4913b17ad130335edc0eb702a8",
File(buildFolder, "yarn.lock.sha").readText())
assertEquals(
"9be5bca432b81becf4f54451aea021add68376330581eaa93ab9a0b3e4e29a3b",
File(buildFolder, "package-lock.json.sha").readText())
}
private fun createJsonFile(@Language("JSON") input: String) =
tempFolder.newFile().apply { writeText(input) }
}

View File

@ -15,4 +15,10 @@ pluginManagement {
plugins { id("org.gradle.toolchains.foojay-resolver-convention").version("0.5.0") }
rootProject.name = "react-native-gradle-plugin"
include(
":react-native-gradle-plugin",
":settings-plugin",
":shared",
)
rootProject.name = "gradle-plugins-root"

View File

@ -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.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins { alias(libs.plugins.kotlin.jvm) }
repositories { mavenCentral() }
group = "com.facebook.react"
dependencies {
implementation(libs.gson)
implementation(libs.guava)
testImplementation(libs.junit)
}
java { targetCompatibility = JavaVersion.VERSION_11 }
kotlin { jvmToolchain(17) }
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions {
apiVersion = "1.6"
jvmTarget = "11"
allWarningsAsErrors = true
}
}
tasks.withType<Test>().configureEach {
testLogging {
exceptionFormat = TestExceptionFormat.FULL
showExceptions = true
showCauses = true
showStackTraces = true
}
}

View File

@ -1,78 +0,0 @@
/*
* 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.tasks
import com.facebook.react.utils.windowsAwareCommandLine
import java.io.FileOutputStream
import org.gradle.api.file.FileCollection
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.*
/**
* A task that will run @react-native-community/cli config if necessary to generate the autolinking
* configuration file.
*/
abstract class RunAutolinkingConfigTask : Exec() {
init {
group = "react"
}
@get:Input abstract val autolinkConfigCommand: ListProperty<String>
/*
* We don't want to re-run config if the lockfiles haven't changed.
* So we have the lockfiles as @InputFiles for this task.
*/
@get:InputFiles abstract val autolinkLockFiles: Property<FileCollection>
@get:InputFile @get:Optional abstract val autolinkConfigFile: RegularFileProperty
@get:OutputFile abstract val autolinkOutputFile: RegularFileProperty
override fun exec() {
wipeOutputDir()
setupCommandLine()
super.exec()
}
internal fun setupCommandLine() {
if (!autolinkConfigFile.isPresent || !autolinkConfigFile.get().asFile.exists()) {
setupConfigCommandLine()
} else {
setupConfigCopyCommandLine()
}
}
internal fun wipeOutputDir() {
autolinkOutputFile.asFile.get().apply {
deleteRecursively()
parentFile.mkdirs()
}
}
internal fun setupConfigCommandLine() {
workingDir(project.projectDir)
standardOutput = FileOutputStream(autolinkOutputFile.get().asFile)
commandLine(
windowsAwareCommandLine(
*autolinkConfigCommand.get().toTypedArray(),
))
}
internal fun setupConfigCopyCommandLine() {
workingDir(project.projectDir)
commandLine(
windowsAwareCommandLine(
"cp",
autolinkConfigFile.get().asFile.absolutePath,
autolinkOutputFile.get().asFile.absolutePath))
}
}

View File

@ -1,159 +0,0 @@
/*
* 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.tasks
import com.facebook.react.tests.createProject
import com.facebook.react.tests.createTestTask
import java.io.File
import java.io.FileOutputStream
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
class RunAutolinkingConfigTaskTest {
@get:Rule val tempFolder = TemporaryFolder()
@Test
fun runAutolinkingConfigTask_groupIsSetCorrectly() {
val task = createTestTask<RunAutolinkingConfigTask> {}
assertEquals("react", task.group)
}
@Test
fun runAutolinkingConfigTask_staticInputs_areSetCorrectly() {
val project = createProject()
val task =
createTestTask<RunAutolinkingConfigTask> {
it.autolinkConfigCommand.set(listOf("rm", "-rf", "/"))
it.autolinkLockFiles.set(project.files("packager.lock", "another-packager.lock"))
it.autolinkConfigFile.set(tempFolder.newFile("dependencies.json"))
it.autolinkOutputFile.set(tempFolder.newFile("output.json"))
}
assertEquals(3, task.inputs.files.files.size)
task.autolinkLockFiles.get().files.forEach {
assertTrue(
it.name == "depedencies.json" ||
it.name == "packager.lock" ||
it.name == "another-packager.lock")
}
assertTrue(task.inputs.properties.containsKey("autolinkConfigCommand"))
assertEquals(1, task.outputs.files.files.size)
assertEquals(File(tempFolder.root, "output.json"), task.outputs.files.singleFile)
assertEquals(listOf("rm", "-rf", "/"), task.autolinkConfigCommand.get())
assertEquals(2, task.autolinkLockFiles.get().files.size)
task.autolinkLockFiles.get().files.forEach {
assertTrue(it.name == "packager.lock" || it.name == "another-packager.lock")
}
assertEquals(File(tempFolder.root, "dependencies.json"), task.autolinkConfigFile.get().asFile)
assertEquals(File(tempFolder.root, "output.json"), task.autolinkOutputFile.get().asFile)
}
@Test
fun wipeOutputDir_worksCorrectly() {
val outputDir =
tempFolder.newFolder("output").apply {
File(this, "output.json").createNewFile()
File(this, "NothingToSeeHere.java").createNewFile()
}
val task = createTestTask<RunAutolinkingConfigTask> { it.autolinkOutputFile.set(outputDir) }
task.wipeOutputDir()
assertFalse(outputDir.exists())
}
@Test
fun setupConfigCommandLine_worksCorrectly() {
val project = createProject()
val task =
createTestTask<RunAutolinkingConfigTask>(project) {
it.autolinkConfigCommand.set(listOf("rm", "-rf", "/"))
it.autolinkOutputFile.set(tempFolder.newFile("output.json"))
}
task.setupConfigCommandLine()
assertEquals(project.projectDir, task.workingDir)
assertTrue(task.standardOutput is FileOutputStream)
assertEquals(listOf("rm", "-rf", "/"), task.commandLine)
}
@Test
fun setupConfigCopyCommandLine_worksCorrectly() {
val project = createProject()
val task =
createTestTask<RunAutolinkingConfigTask>(project) {
it.autolinkConfigFile.set(tempFolder.newFile("dependencies.json"))
it.autolinkOutputFile.set(tempFolder.newFile("output.json"))
}
task.setupConfigCopyCommandLine()
assertEquals(project.projectDir, task.workingDir)
assertTrue(task.standardOutput !is FileOutputStream)
assertEquals("cp", task.commandLine[0])
assertEquals(File(tempFolder.root, "dependencies.json").absolutePath, task.commandLine[1])
assertEquals(File(tempFolder.root, "output.json").absolutePath, task.commandLine[2])
}
@Test
fun setupCommandLine_withoutAutolinkConfigFileConfigured_invokesCommand() {
val project = createProject()
val task =
createTestTask<RunAutolinkingConfigTask>(project) {
it.autolinkConfigCommand.set(listOf("rm", "-rf", "/"))
it.autolinkOutputFile.set(tempFolder.newFile("output.json"))
}
task.setupCommandLine()
assertEquals(listOf("rm", "-rf", "/"), task.commandLine)
}
@Test
fun setupCommandLine_withoutMissingConfigFile_invokesCommand() {
val project = createProject()
val task =
createTestTask<RunAutolinkingConfigTask>(project) {
it.autolinkConfigCommand.set(listOf("rm", "-rf", "/"))
it.autolinkConfigFile.set(File(tempFolder.root, "dependencies.json"))
it.autolinkOutputFile.set(tempFolder.newFile("output.json"))
}
task.setupCommandLine()
assertEquals(listOf("rm", "-rf", "/"), task.commandLine)
}
@Test
fun setupCommandLine_withoutExistingConfigFile_invokesCp() {
val project = createProject()
val configFile = tempFolder.newFile("dependencies.json").apply { writeText("¯\\_(ツ)_/¯") }
val task =
createTestTask<RunAutolinkingConfigTask>(project) {
it.autolinkConfigCommand.set(listOf("rm", "-rf", "/"))
it.autolinkConfigFile.set(configFile)
it.autolinkOutputFile.set(tempFolder.newFile("output.json"))
}
task.setupCommandLine()
assertEquals(
listOf("cp", configFile.absolutePath, File(tempFolder.root, "output.json").absolutePath),
task.commandLine)
}
}

View File

@ -0,0 +1,49 @@
# 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.
cmake_minimum_required(VERSION 3.13)
set(CMAKE_VERBOSE_MAKEFILE on)
file(GLOB react_codegen_SRCS CONFIGURE_DEPENDS *.cpp react/renderer/components/ReactPopupMenuAndroidSpecs/*.cpp)
add_library(
react_codegen_ReactPopupMenuAndroidSpecs
SHARED
${react_codegen_SRCS}
)
target_include_directories(react_codegen_ReactPopupMenuAndroidSpecs PUBLIC . react/renderer/components/ReactPopupMenuAndroidSpecs)
target_link_libraries(
react_codegen_ReactPopupMenuAndroidSpecs
fbjni
folly_runtime
glog
jsi
react_codegen_rncore
react_debug
react_nativemodule_core
react_render_componentregistry
react_render_core
react_render_debug
react_render_graphics
react_render_imagemanager
react_render_mapbuffer
react_utils
rrc_image
rrc_view
turbomodulejsijni
yoga
)
target_compile_options(
react_codegen_ReactPopupMenuAndroidSpecs
PRIVATE
-DLOG_TAG=\"ReactNative\"
-fexceptions
-frtti
-std=c++20
-Wall
)

View File

@ -0,0 +1,22 @@
/**
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
*
* Do not edit this file as changes may cause incorrect behavior and will be lost
* once the code is regenerated.
*
* @generated by codegen project: GenerateModuleJniCpp.js
*/
#include "ReactPopupMenuAndroidSpecs.h"
namespace facebook::react {
std::shared_ptr<TurboModule> ReactPopupMenuAndroidSpecs_ModuleProvider(const std::string &moduleName, const JavaTurboModule::InitParams &params) {
return nullptr;
}
} // namespace facebook::react

View File

@ -0,0 +1,24 @@
/**
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
*
* Do not edit this file as changes may cause incorrect behavior and will be lost
* once the code is regenerated.
*
* @generated by codegen project: GenerateModuleJniH.js
*/
#pragma once
#include <ReactCommon/JavaTurboModule.h>
#include <ReactCommon/TurboModule.h>
#include <jsi/jsi.h>
namespace facebook::react {
JSI_EXPORT
std::shared_ptr<TurboModule> ReactPopupMenuAndroidSpecs_ModuleProvider(const std::string &moduleName, const JavaTurboModule::InitParams &params);
} // namespace facebook::react

View File

@ -0,0 +1,16 @@
/**
* 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.
*/
module.exports = {
dependency: {
platforms: {
android: {
'cmakeListsPath': '../android/src/main/jni/CMakeLists.txt',
},
},
},
};

View File

@ -0,0 +1,16 @@
/**
* 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.
*/
module.exports = {
dependency: {
platforms: {
android: {
'cmakeListsPath': '../android/src/main/jni/CMakeLists.txt',
},
},
},
};

View File

@ -37,14 +37,14 @@ endif()
file(GLOB input_SRC CONFIGURE_DEPENDS
*.cpp
${BUILD_DIR}/generated/rncli/src/main/jni/*.cpp)
${BUILD_DIR}/generated/autolinking/src/main/jni/*.cpp)
add_library(${CMAKE_PROJECT_NAME} SHARED ${input_SRC})
target_include_directories(${CMAKE_PROJECT_NAME}
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
${PROJECT_BUILD_DIR}/generated/rncli/src/main/jni)
${PROJECT_BUILD_DIR}/generated/autolinking/src/main/jni)
target_compile_options(${CMAKE_PROJECT_NAME}
PRIVATE
@ -126,8 +126,8 @@ target_compile_options(common_flags INTERFACE ${folly_FLAGS})
target_link_libraries(ReactAndroid::react_codegen_rncore INTERFACE common_flags)
# If project is on RN CLI v9, then we can use the following lines to link against the autolinked 3rd party libraries.
if(EXISTS ${PROJECT_BUILD_DIR}/generated/rncli/src/main/jni/Android-rncli.cmake)
include(${PROJECT_BUILD_DIR}/generated/rncli/src/main/jni/Android-rncli.cmake)
if(EXISTS ${PROJECT_BUILD_DIR}/generated/autolinking/src/main/jni/Android-autolinking.cmake)
include(${PROJECT_BUILD_DIR}/generated/autolinking/src/main/jni/Android-autolinking.cmake)
target_link_libraries(${CMAKE_PROJECT_NAME} ${AUTOLINKED_LIBRARIES})
foreach(autolinked_library ${AUTOLINKED_LIBRARIES})
target_link_libraries(${autolinked_library} common_flags)

View File

@ -49,6 +49,9 @@ react {
//
// The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
// hermesFlags = ["-O", "-output-source-map"]
/* Autolinking */
autolinkLibrariesWithApp()
}
/**
@ -114,5 +117,3 @@ dependencies {
implementation jscFlavor
}
}
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)

View File

@ -1,7 +1,7 @@
package com.helloworld
import android.app.Application
import com.facebook.react.PackageList
import com.facebook.react.PackageList2
import com.facebook.react.ReactApplication
import com.facebook.react.ReactHost
import com.facebook.react.ReactNativeHost
@ -16,7 +16,7 @@ class MainApplication : Application(), ReactApplication {
override val reactNativeHost: ReactNativeHost =
object : DefaultReactNativeHost(this) {
override fun getPackages(): List<ReactPackage> =
PackageList(this).packages.apply {
PackageList2(this).packages.apply {
// Packages that cannot be autolinked yet can be added manually here, for example:
// add(MyReactNativePackage())
}

Some files were not shown because too many files have changed in this diff Show More