Skip to content

Commit

Permalink
RNGP - Move the generateAutolinkingNewArchitectureFiles task to core …
Browse files Browse the repository at this point in the history
…autolinking

Summary:
This diff is part of RFC0759
react-native-community/discussions-and-proposals#759

Here I'm moving the New Architecture C++ Autolinking from the CLI to core.
It follows the same logic as this:
https://github.com/react-native-community/cli/blob/73f880c3d87cdde81204364289f2f488a473c52b/packages/cli-platform-android/native_modules.gradle#L544-L550

Changelog:
[Internal] [Changed] - RNGP - Move the generateAutolinkingNewArchitectureFiles task to core autolinking

Reviewed By: cipolleschi, blakef

Differential Revision: D55475594

fbshipit-source-id: d3768d932032e8e98f992d5f1bc54c439520873d
  • Loading branch information
cortinico authored and kosmydel committed May 6, 2024
1 parent 2fdef74 commit e6d1a71
Show file tree
Hide file tree
Showing 4 changed files with 479 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ 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
Expand All @@ -25,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 @@ -222,6 +224,8 @@ class ReactPlugin : Plugin<Project> {
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 =
Expand All @@ -244,6 +248,21 @@ class ReactPlugin : Plugin<Project> {
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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ data class ModelAutolinkingDependenciesPlatformAndroidJson(
val buildTypes: List<String>,
val libraryName: String,
val componentDescriptors: List<String>,
val cmakeListsPath: String,
val cmakeListsPath: String? = null,
val cxxModuleCMakeListsModuleName: String? = null,
val cxxModuleCMakeListsPath: String? = null,
val cxxModuleHeaderName: String? = null,
Expand Down
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()
}
}
Loading

0 comments on commit e6d1a71

Please sign in to comment.