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

feat: autolinking for the new architecture on Android #1603

Merged
merged 13 commits into from
May 18, 2022

Conversation

thymikee
Copy link
Member

@thymikee thymikee commented May 6, 2022

Summary:

This PR introduces an initial support for autolinking the new architecture for Android platform. iOS is already handled AFAIK.

Closes #1596

High-level overview of how it works

When the new architecture is turned on, the generateNewArchitectureFiles task is fired, generating /android/build/generated/rn/src/main/jni directory with the following files:

  • Android-rncli.mk – creates a list of codegen'd libs. Used by the project's Android.mk.
  • rncli.cpp – registers codegen'd Turbo Modules and Fabric component providers. Used by MainApplicationModuleProvider.cpp and MainComponentsRegistry.cpp.
  • rncli.h - a header file for rncli.cpp.

This change requires updating the default template in react-native repository. PR: facebook/react-native#33777

Test Plan:

So far I've been testing it with following libraries:

  • https://github.com/react-native-community/RNNewArchitectureLibraries/tree/feat/back-turbomodule
  • https://github.com/react-native-community/RNNewArchitectureLibraries/tree/feat/back-fabric-comp
  • react-native-screens, react-native-safe-area-context through React Navigation – RNScreens provides a custom setup which bypasses autolinking through obfuscating its configuration. To have it compile we need to opt-out from autolinking by nulling added properties, like so:
    module.exports = {
      dependencies: {
        'react-native-screens': {
          platforms: {
            android: {
              libraryName: null,
              componentDescriptors: null,
              androidMkPath: null,
            },
          },
        },
        'react-native-safe-area-context': {
          platforms: {
            android: {
              libraryName: null,
              componentDescriptors: null,
              androidMkPath: null,
            },
          },
        },
      },
    };
    I've only been able to build RNScreens under RN 0.68. It failed with "Android NDK: Module rnscreens_common depends on undefined modules: folly_json folly_futures" under RN 0.69 RC0. It also needed this patch:
    diff --git a/node_modules/react-native-screens/android/build.gradle b/node_modules/react-native-screens/android/build.gradle
    index 5097a1a..7ceb834 100644
    --- a/node_modules/react-native-screens/android/build.gradle
    +++ b/node_modules/react-native-screens/android/build.gradle
    @@ -35,6 +35,15 @@ apply plugin: 'kotlin-android'
     android {
         compileSdkVersion safeExtGet('compileSdkVersion', 28)
     
    +    // Used to override the NDK path/version on internal CI or by allowing
    +    // users to customize the NDK path/version from their root project (e.g. for M1 support)
    +    if (rootProject.hasProperty("ndkPath")) {
    +        ndkPath rootProject.ext.ndkPath
    +    }
    +    if (rootProject.hasProperty("ndkVersion")) {
    +        ndkVersion rootProject.ext.ndkVersion
    +    }
    +
         defaultConfig {
             minSdkVersion safeExtGet('minSdkVersion', 16)
             targetSdkVersion safeExtGet('targetSdkVersion', 22)
    

How to test it:

  1. Init a RN 0.68+ app
  2. Link RN CLI libraries (instructions in CONTRIBUTING.md)
  3. Apply the following changes to the initialized project:
diff --git a/android/app/src/main/jni/Android.mk b/android/app/src/main/jni/Android.mk
index becd71a..6385ed9 100644
--- a/android/app/src/main/jni/Android.mk
+++ b/android/app/src/main/jni/Android.mk
@@ -5,6 +5,10 @@ include $(REACT_ANDROID_DIR)/Android-prebuilt.mk
 # If you wish to add a custom TurboModule or Fabric component in your app you
 # will have to include the following autogenerated makefile.
 # include $(GENERATED_SRC_DIR)/codegen/jni/Android.mk
+
+# Includes the MK file for autolinked libraries
+include $(PROJECT_BUILD_DIR)/generated/rncli/src/main/jni/Android-rncli.mk  
+
 include $(CLEAR_VARS)
 
 LOCAL_PATH := $(THIS_DIR)
@@ -16,6 +20,11 @@ LOCAL_C_INCLUDES := $(LOCAL_PATH)
 LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp)
 LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
 
+# Autolinked TurboModules and Fabric components
+LOCAL_C_INCLUDES += $(PROJECT_BUILD_DIR)/generated/rncli/src/main/jni
+LOCAL_SRC_FILES += $(wildcard $(PROJECT_BUILD_DIR)/generated/rncli/src/main/jni/*.cpp)
+LOCAL_EXPORT_C_INCLUDES += $(PROJECT_BUILD_DIR)/generated/rncli/src/main/jni
+
 # If you wish to add a custom TurboModule or Fabric component in your app you
 # will have to uncomment those lines to include the generated source 
 # files from the codegen (placed in $(GENERATED_SRC_DIR)/codegen/jni)
@@ -44,6 +53,9 @@ LOCAL_SHARED_LIBRARIES := \
   libturbomodulejsijni \
   libyoga
 
+# Autolinked libraries
+LOCAL_SHARED_LIBRARIES += $(call import-codegen-modules) 
+
 LOCAL_CFLAGS := -DLOG_TAG=\"ReactNative\" -fexceptions -frtti -std=c++17 -Wall
 
 include $(BUILD_SHARED_LIBRARY)
diff --git a/android/app/src/main/jni/MainApplicationModuleProvider.cpp b/android/app/src/main/jni/MainApplicationModuleProvider.cpp
index 0ac23cc..b961e35 100644
--- a/android/app/src/main/jni/MainApplicationModuleProvider.cpp
+++ b/android/app/src/main/jni/MainApplicationModuleProvider.cpp
@@ -1,6 +1,7 @@
 #include "MainApplicationModuleProvider.h"
 
 #include <rncore.h>
+#include <rncli.h>
 
 namespace facebook {
 namespace react {
@@ -17,6 +18,13 @@ std::shared_ptr<TurboModule> MainApplicationModuleProvider(
   //    return module;
   // }
   // return rncore_ModuleProvider(moduleName, params);
+
+  // Module providers autolinked by RN CLI  
+  auto rncli_module = rncli_ModuleProvider(moduleName, params);
+  if (rncli_module != nullptr) {
+     return rncli_module;
+  }
+  
   return rncore_ModuleProvider(moduleName, params);
 }
 
diff --git a/android/app/src/main/jni/MainComponentsRegistry.cpp b/android/app/src/main/jni/MainComponentsRegistry.cpp
index 8f7edff..12ba43a 100644
--- a/android/app/src/main/jni/MainComponentsRegistry.cpp
+++ b/android/app/src/main/jni/MainComponentsRegistry.cpp
@@ -4,6 +4,7 @@
 #include <fbjni/fbjni.h>
 #include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
 #include <react/renderer/components/rncore/ComponentDescriptors.h>
+#include <rncli.h>
 
 namespace facebook {
 namespace react {
@@ -14,6 +15,9 @@ std::shared_ptr<ComponentDescriptorProviderRegistry const>
 MainComponentsRegistry::sharedProviderRegistry() {
   auto providerRegistry = CoreComponentsRegistry::sharedProviderRegistry();
 
+  // Providers registered automatically by RN CLI
+  rncli_registerProviders(providerRegistry);
+  
   // Custom Fabric Components go here. You can register custom
   // components coming from your App or from 3rd party libraries here.
   //
  1. Add your fabric component or turbo module and verify that android/build/generated/rn/src/main/jni is populated with Android-rncli.mk, rncli.cpp and rncli.h, correctly referencing your library.
  2. Verify that yarn react-native config contains 3 newly added config keys with correct values. E.g.:
    {
      "dependencies": {
        "example-component": {
          "root": "<PROJECT_ROOT>/node_modules/example-component",
          "name": "example-component",
          "platforms": {
            "ios": null,
            "android": {
              "sourceDir": "<PROJECT_ROOT>/node_modules/example-component/android",
              "packageImportPath": "import com.rnnewarchitecturelibrary.ColoredViewPackage;",
              "packageInstance": "new ColoredViewPackage()",
              "buildTypes": [],
              "libraryName": "colorview",
              "componentDescriptors": ["ColoredViewComponentDescriptor"],
              "androidMkPath": "<PROJECT_ROOT>/node_modules/example-component/android/build/generated/source/codegen/jni/Android.mk"
            }
          }
        }
      }
    }
    

@thymikee thymikee changed the title feat: autolinking for the new architecture feat: autolinking for the new architecture on Android May 6, 2022
Copy link
Member

@cortinico cortinico left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did a first pass. left a couple of minor comments.

Q: Are those:

  libraryName?: string;
  componentDescriptors?: string[];
  androidMkPath?: string;

configurable via npx react-native config?

packages/platform-android/native_modules.gradle Outdated Show resolved Hide resolved
packages/platform-android/native_modules.gradle Outdated Show resolved Hide resolved
rncliCppIncludes = packages.collect {
def result = "#include <${it.libraryName}.h>"
if (it.componentDescriptors.size() > 0) {
result += "\n#include <react/renderer/components/${it.libraryName}/ComponentDescriptors.h>"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two things here:

  1. Please export to a constant the react/renderer/components/ and ComponentDescriptors.h
  2. There might be some escaping to do. I'll check and get back to you


task generatePackageList {
doLast {
autoModules.generatePackagesFile(generatedCodeDir, generatedFileName, generatedFileContentsTemplate)
}
}

task generateNewArchitectureFiles {
doLast {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would refrain from using doLast. Those are terrible from the caching point of view (as they invalidate the gradle caches). We should move this to a proper task.

Not a blocker though :), just a heads up

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure! I'm not a heavy user of Gradle, so I'd appreciate any contributions in code or pointers on what to leverage exactly :)

packages/platform-android/native_modules.gradle Outdated Show resolved Hide resolved
@thymikee
Copy link
Member Author

thymikee commented May 8, 2022

Q: Are those:
libraryName?: string;
componentDescriptors?: string[];
androidMkPath?: string;
configurable via npx react-native config?

@cortinico yup, although in current implementation they're only configurable for libraries themselves. It's not yet possible to override this from user's configuration (I'm not yet sure if it's worth the complexity, but I acknowledge it may be for the time of transition)

EDIT: it's now possible to override both from library or app config.

@thymikee
Copy link
Member Author

Made it work for libraries with their own autolinking setup (RNScreens, RNSafeAreaContext). This is ready to be published in next major version. Planned for this week.

@thymikee
Copy link
Member Author

Released in v9.0.0-alpha.0

facebook-github-bot pushed a commit to facebook/react-native that referenced this pull request Jun 24, 2022
Summary:
Provides necessary changes for the autolinking to work in new architecture on Android. Depends on react-native-community/cli#1603 and is subject to change.

Upgraded the RN CLI to v9.0.0-alpha.0 so that it's testable locally.

## Changelog

<!-- Help reviewers and the release process by writing your own changelog entry. For an example, see:
https://github.com/facebook/react-native/wiki/Changelog
-->

[Android] [Change] - Adapt template to new architecture autolinking on Android

Pull Request resolved: #33777

Test Plan: CI

Reviewed By: cipolleschi

Differential Revision: D36478984

Pulled By: cortinico

fbshipit-source-id: 970fa7bcb77898d9defae18c20026a7783ba4108
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

autolinking: Support for Android on New Architecture
2 participants