Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RNGP - Move the generateAutolinkingNewArchitectureFiles task to core autolinking #44289

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ package com.facebook.react
import com.android.build.api.variant.AndroidComponentsExtension
import com.android.build.gradle.internal.tasks.factory.dependsOn
import com.facebook.react.internal.PrivateReactExtension
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
Expand All @@ -24,6 +26,7 @@ import com.facebook.react.utils.DependencyUtils.readVersionAndGroupStrings
import com.facebook.react.utils.JdkConfiguratorUtils.configureJavaToolChains
import com.facebook.react.utils.JsonUtils
import com.facebook.react.utils.NdkConfiguratorUtils.configureReactNativeNdk
import com.facebook.react.utils.ProjectUtils.isNewArchEnabled
import com.facebook.react.utils.ProjectUtils.needsCodegenFromPackageJson
import com.facebook.react.utils.ProjectUtils.shouldWarnIfNewArchFlagIsSetInPrealpha
import com.facebook.react.utils.findPackageJsonFile
Expand Down Expand Up @@ -219,13 +222,58 @@ class ReactPlugin : Plugin<Project> {
) {
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")

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)
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)
}

// 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
// dependency.
val generatePackageListTask =
project.tasks.register(
"generateAutolinkingPackageList", GeneratePackageListTask::class.java) { task ->
task.dependsOn(runAutolinkingConfigTask)
task.autolinkInputFile.set(configOutputFile)
task.generatedOutputDirectory.set(generatedAutolinkingJavaDir)
}

if (project.isNewArchEnabled(extension)) {
// For New Arch, we also need to generate code for C++ Autolinking
val generateAutolinkingNewArchitectureFilesTask =
project.tasks.register(
"generateAutolinkingNewArchitectureFiles",
GenerateAutolinkingNewArchitecturesFileTask::class.java) { task ->
task.dependsOn(runAutolinkingConfigTask)
task.autolinkInputFile.set(configOutputFile)
task.generatedOutputDirectory.set(generatedAutolinkingJniDir)
}
project.tasks
.named("preBuild", Task::class.java)
.dependsOn(generateAutolinkingNewArchitectureFilesTask)
}

// We let generateAutolinkingPackageList depend on the preBuild task so it's executed before
// everything else.
project.tasks.named("preBuild", Task::class.java).dependsOn(generatePackageListTask)

// We tell Android Gradle Plugin that inside /build/generated/autolinking/src/main/java there
// are sources to be compiled as well.
project.extensions.getByType(AndroidComponentsExtension::class.java).apply {
onVariants(selector().all()) { variant ->
variant.sources.java?.addStaticSourceDirectory(
generatedAutolinkingJavaDir.get().asFile.absolutePath)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ data class ModelAutolinkingDependenciesPlatformAndroidJson(
val buildTypes: List<String>,
val libraryName: String,
val componentDescriptors: List<String>,
val cmakeListsPath: String,
val cxxModuleCMakeListsModuleName: String?,
val cxxModuleCMakeListsPath: String?,
val cxxModuleHeaderName: String?,
val dependencyConfiguration: String?
val cmakeListsPath: String? = null,
val cxxModuleCMakeListsModuleName: String? = null,
val cxxModuleCMakeListsPath: String? = null,
val cxxModuleHeaderName: String? = null,
val dependencyConfiguration: String? = null
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
/*
* 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.model.ModelAutolinkingDependenciesJson
import com.facebook.react.utils.JsonUtils
import java.io.File
import org.gradle.api.DefaultTask
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction

abstract class GenerateAutolinkingNewArchitecturesFileTask : DefaultTask() {

init {
group = "react"
}

@get:InputFile abstract val autolinkInputFile: RegularFileProperty

@get:OutputDirectory abstract val generatedOutputDirectory: DirectoryProperty

@TaskAction
fun taskAction() {
val model = JsonUtils.fromAutolinkingConfigJson(autolinkInputFile.get().asFile)

val packages = model?.dependencies?.values ?: emptyList()

val cmakeFileContent = generateCmakeFileContent(packages)
val cppFileContent = generateCppFileContent(packages)

val outputDir = generatedOutputDirectory.get().asFile
outputDir.mkdirs()
File(outputDir, CMAKE_FILENAME).apply { writeText(cmakeFileContent) }
File(outputDir, CPP_FILENAME).apply { writeText(cppFileContent) }
File(outputDir, H_FILENAME).apply { writeText(hTemplate) }
}

internal fun generateCmakeFileContent(
packages: Collection<ModelAutolinkingDependenciesJson>
): String {
val libraryIncludes =
packages.joinToString("\n") { dep ->
var addDirectoryString = ""
if (dep.platforms?.android?.libraryName != null &&
dep.platforms.android.cmakeListsPath != null) {
// If user provided a custom cmakeListsPath, let's honor it.
val nativeFolderPath =
dep.platforms.android.cmakeListsPath.replace("CMakeLists.txt", "")
addDirectoryString +=
"add_subdirectory($nativeFolderPath ${dep.platforms.android.libraryName}_autolinked_build)"
}
if (dep.platforms?.android?.cxxModuleCMakeListsPath != null) {
// If user provided a custom cxxModuleCMakeListsPath, let's honor it.
val nativeFolderPath =
dep.platforms.android.cxxModuleCMakeListsPath.replace("CMakeLists.txt", "")
addDirectoryString +=
"\nadd_subdirectory($nativeFolderPath ${dep.platforms.android.libraryName}_cxxmodule_autolinked_build)"
}
addDirectoryString
}

val libraryModules =
packages.joinToString("\n ") { dep ->
var autolinkedLibraries = ""
if (dep.platforms?.android?.libraryName != null) {
autolinkedLibraries += "$CODEGEN_LIB_PREFIX${dep.platforms.android.libraryName}"
}
if (dep.platforms?.android?.cxxModuleCMakeListsModuleName != null) {
autolinkedLibraries += "\n${dep.platforms.android.cxxModuleCMakeListsModuleName}"
}
autolinkedLibraries
}

return CMAKE_TEMPLATE.replace("{{ libraryIncludes }}", libraryIncludes)
.replace("{{ libraryModules }}", libraryModules)
}

internal fun generateCppFileContent(
packages: Collection<ModelAutolinkingDependenciesJson>
): String {
val packagesWithLibraryNames = packages.filter { it.platforms?.android?.libraryName != null }

val cppIncludes =
packagesWithLibraryNames.joinToString("\n") { dep ->
var include = "#include <${dep.platforms?.android?.libraryName}.h>"
if (dep.platforms?.android?.componentDescriptors != null &&
dep.platforms.android.componentDescriptors.isNotEmpty()) {
include +=
"\n#include <${COMPONENT_INCLUDE_PATH}/${dep.platforms.android.libraryName}/${COMPONENT_DESCRIPTOR_FILENAME}>"
}
if (dep.platforms?.android?.cxxModuleHeaderName != null) {
include += "\n#include <${dep.platforms.android.cxxModuleHeaderName}.h>"
}
include
}

val cppTurboModuleJavaProviders =
packagesWithLibraryNames.joinToString("\n") { dep ->
val libraryName = dep.platforms?.android?.libraryName
// language=cpp
"""
auto module_$libraryName = ${libraryName}_ModuleProvider(moduleName, params);
if (module_$libraryName != nullptr) {
return module_$libraryName;
}
"""
.trimIndent()
}

val cppTurboModuleCxxProviders =
packagesWithLibraryNames
.filter { it.platforms?.android?.cxxModuleHeaderName != null }
.joinToString("\n") { dep ->
val cxxModuleHeaderName = dep.platforms?.android?.cxxModuleHeaderName
// language=cpp
"""
if (moduleName == $cxxModuleHeaderName::kModuleName) {
return std::make_shared<$cxxModuleHeaderName>(jsInvoker);
}
"""
.trimIndent()
}

val cppComponentDescriptors =
packagesWithLibraryNames
.filter {
it.platforms?.android?.componentDescriptors != null &&
it.platforms.android.componentDescriptors.isNotEmpty()
}
.joinToString("\n") {
it.platforms?.android?.componentDescriptors?.joinToString("\n") {
"providerRegistry->add(concreteComponentDescriptorProvider<$it>());"
} ?: ""
}

return CPP_TEMPLATE.replace("{{ rncliCppIncludes }}", cppIncludes)
.replace("{{ rncliCppTurboModuleJavaProviders }}", cppTurboModuleJavaProviders)
.replace("{{ rncliCppTurboModuleCxxProviders }}", cppTurboModuleCxxProviders)
.replace("{{ rncliCppComponentDescriptors }}", cppComponentDescriptors)
}

companion object {
const val CMAKE_FILENAME = "Android-autolinking.cmake"

// This needs to be changed to not be `rncli.h`, but requires change to CMake pipeline
const val H_FILENAME = "rncli.h"
const val CPP_FILENAME = "rncli.cpp"

const val CODEGEN_LIB_PREFIX = "react_codegen_"

const val COMPONENT_DESCRIPTOR_FILENAME = "ComponentDescriptors.h"
const val COMPONENT_INCLUDE_PATH = "react/renderer/components"

// language=cmake
val CMAKE_TEMPLATE =
"""
# This code was generated by [React Native](https://www.npmjs.com/package/@react-native/gradle-plugin)
cmake_minimum_required(VERSION 3.13)
set(CMAKE_VERBOSE_MAKEFILE on)

{{ libraryIncludes }}

set(AUTOLINKED_LIBRARIES
{{ libraryModules }}
)
"""
.trimIndent()

// language=cpp
val CPP_TEMPLATE =
"""
/**
* This code was generated by [React Native](https://www.npmjs.com/package/@react-native/gradle-plugin).
*
* Do not edit this file as changes may cause incorrect behavior and will be lost
* once the code is regenerated.
*
*/

#include "rncli.h"
{{ rncliCppIncludes }}

namespace facebook {
namespace react {

std::shared_ptr<TurboModule> rncli_ModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams &params) {
{{ rncliCppTurboModuleJavaProviders }}
return nullptr;
}

std::shared_ptr<TurboModule> rncli_cxxModuleProvider(const std::string moduleName, const std::shared_ptr<CallInvoker>& jsInvoker) {
{{ rncliCppTurboModuleCxxProviders }}
return nullptr;
}

void rncli_registerProviders(std::shared_ptr<ComponentDescriptorProviderRegistry const> providerRegistry) {
{{ rncliCppComponentDescriptors }}
return;
}

} // namespace react
} // namespace facebook
"""
.trimIndent()

// language=cpp
val hTemplate =
"""
/**
* This code was generated by [React Native](https://www.npmjs.com/package/@react-native/gradle-plugin).
*
* Do not edit this file as changes may cause incorrect behavior and will be lost
* once the code is regenerated.
*
*/

#pragma once

#include <ReactCommon/CallInvoker.h>
#include <ReactCommon/JavaTurboModule.h>
#include <ReactCommon/TurboModule.h>
#include <jsi/jsi.h>
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>

namespace facebook {
namespace react {

std::shared_ptr<TurboModule> rncli_ModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams &params);
std::shared_ptr<TurboModule> rncli_cxxModuleProvider(const std::string moduleName, const std::shared_ptr<CallInvoker>& jsInvoker);
void rncli_registerProviders(std::shared_ptr<ComponentDescriptorProviderRegistry const> providerRegistry);

} // namespace react
} // namespace facebook
"""
.trimIndent()
}
}