Skip to content

Commit 2ff08e8

Browse files
cortinicofacebook-github-bot
authored andcommitted
RNGP - Do the .so cleanup using pickFirst and exclude (#35093)
Summary: Pull Request resolved: #35093 It turns out that using the Artifacts API to manipulate the APK to remove .so has unintended side effects and is causing the `installDebug` and `installRelease` commands to fail. I've resorted to register a packaging option for each variant to make sure we include only the correct artifacts we want. This should fix the current startup crash that is experienced on main. Changelog: [Android] [Fixed] - RNGP - Do the .so cleanup using pickFirst and exclude Reviewed By: cipolleschi Differential Revision: D40722285 fbshipit-source-id: 982e1e9c474522fc4419c969ede5ee14e5404f3a
1 parent 7d61b9d commit 2ff08e8

File tree

9 files changed

+231
-661
lines changed

9 files changed

+231
-661
lines changed

packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/TaskConfiguration.kt

Lines changed: 12 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,10 @@
77

88
package com.facebook.react
99

10-
import com.android.build.api.artifact.SingleArtifact
1110
import com.android.build.api.variant.Variant
1211
import com.facebook.react.tasks.BundleHermesCTask
13-
import com.facebook.react.tasks.NativeLibraryAabCleanupTask
14-
import com.facebook.react.tasks.NativeLibraryApkCleanupTask
12+
import com.facebook.react.utils.NdkConfiguratorUtils.configureJsEnginePackagingOptions
13+
import com.facebook.react.utils.NdkConfiguratorUtils.configureNewArchPackagingOptions
1514
import com.facebook.react.utils.ProjectUtils.isHermesEnabled
1615
import com.facebook.react.utils.detectedCliPath
1716
import com.facebook.react.utils.detectedEntryFile
@@ -36,16 +35,20 @@ internal fun Project.configureReactTasks(variant: Variant, config: ReactExtensio
3635
// Additional node and packager commandline arguments
3736
val cliPath = detectedCliPath(project.projectDir, config)
3837

39-
val enableHermesInProject = project.isHermesEnabled
40-
val enableHermesInThisVariant =
38+
val isHermesEnabledInProject = project.isHermesEnabled
39+
val isHermesEnabledInThisVariant =
4140
if (config.enableHermesOnlyInVariants.get().isNotEmpty()) {
42-
config.enableHermesOnlyInVariants.get().contains(variant.name) && enableHermesInProject
41+
config.enableHermesOnlyInVariants.get().contains(variant.name) && isHermesEnabledInProject
4342
} else {
44-
enableHermesInProject
43+
isHermesEnabledInProject
4544
}
4645
val isDebuggableVariant =
4746
config.debuggableVariants.get().any { it.equals(variant.name, ignoreCase = true) }
4847

48+
configureNewArchPackagingOptions(project, variant)
49+
configureJsEnginePackagingOptions(
50+
config, variant, isHermesEnabledInThisVariant, isDebuggableVariant)
51+
4952
if (!isDebuggableVariant) {
5053
val bundleTask =
5154
tasks.register("createBundle${targetName}JsAndAssets", BundleHermesCTask::class.java) {
@@ -59,8 +62,8 @@ internal fun Project.configureReactTasks(variant: Variant, config: ReactExtensio
5962
it.bundleAssetName.set(config.bundleAssetName)
6063
it.jsBundleDir.set(jsBundleDir)
6164
it.resourcesDir.set(resourcesDir)
62-
it.hermesEnabled.set(enableHermesInThisVariant)
63-
it.minifyEnabled.set(!enableHermesInThisVariant)
65+
it.hermesEnabled.set(isHermesEnabledInThisVariant)
66+
it.minifyEnabled.set(!isHermesEnabledInThisVariant)
6467
it.devEnabled.set(false)
6568
it.jsIntermediateSourceMapsDir.set(jsIntermediateSourceMapsDir)
6669
it.jsSourceMapsDir.set(jsSourceMapsDir)
@@ -71,31 +74,4 @@ internal fun Project.configureReactTasks(variant: Variant, config: ReactExtensio
7174
variant.sources.res?.addGeneratedSourceDirectory(bundleTask, BundleHermesCTask::resourcesDir)
7275
variant.sources.assets?.addGeneratedSourceDirectory(bundleTask, BundleHermesCTask::jsBundleDir)
7376
}
74-
75-
if (config.enableSoCleanup.get()) {
76-
val nativeLibraryApkCleanupTask =
77-
project.tasks.register(
78-
"nativeLibrary${targetName}ApkCleanup", NativeLibraryApkCleanupTask::class.java) {
79-
it.debuggableVariant.set(isDebuggableVariant)
80-
it.enableHermes.set(enableHermesInThisVariant)
81-
}
82-
val nativeLibraryBundleCleanupTask =
83-
project.tasks.register(
84-
"nativeLibrary${targetName}BundleCleanup", NativeLibraryAabCleanupTask::class.java) {
85-
it.debuggableVariant.set(isDebuggableVariant)
86-
it.enableHermes.set(enableHermesInThisVariant)
87-
}
88-
89-
variant.artifacts
90-
.use(nativeLibraryApkCleanupTask)
91-
.wiredWithDirectories(
92-
NativeLibraryApkCleanupTask::inputApkDirectory,
93-
NativeLibraryApkCleanupTask::outputApkDirectory)
94-
.toTransform(SingleArtifact.APK)
95-
variant.artifacts
96-
.use(nativeLibraryBundleCleanupTask)
97-
.wiredWithFiles(
98-
NativeLibraryAabCleanupTask::inputBundle, NativeLibraryAabCleanupTask::outputBundle)
99-
.toTransform(SingleArtifact.BUNDLE)
100-
}
10177
}

packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/NativeLibraryAabCleanupTask.kt

Lines changed: 0 additions & 40 deletions
This file was deleted.

packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/NativeLibraryApkCleanupTask.kt

Lines changed: 0 additions & 41 deletions
This file was deleted.

packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/NdkConfiguratorUtils.kt

Lines changed: 131 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
package com.facebook.react.utils
99

1010
import com.android.build.api.variant.AndroidComponentsExtension
11+
import com.android.build.api.variant.Variant
1112
import com.facebook.react.ReactExtension
1213
import com.facebook.react.utils.ProjectUtils.isNewArchEnabled
1314
import java.io.File
@@ -19,83 +20,140 @@ internal object NdkConfiguratorUtils {
1920
project.pluginManager.withPlugin("com.android.application") {
2021
project.extensions.getByType(AndroidComponentsExtension::class.java).finalizeDsl { ext ->
2122
if (!project.isNewArchEnabled) {
22-
// For Old Arch, we set a pickFirst only on libraries that we know are
23-
// clashing with our direct dependencies (FBJNI, Flipper and Hermes).
24-
ext.packagingOptions.jniLibs.pickFirsts.addAll(
25-
listOf(
26-
"**/libfbjni.so",
27-
"**/libc++_shared.so",
28-
))
29-
} else {
30-
// We enable prefab so users can consume .so/headers from ReactAndroid and hermes-engine
31-
// .aar
32-
ext.buildFeatures.prefab = true
33-
34-
// We set some packagingOptions { pickFirst ... } for our users for libraries we own.
35-
ext.packagingOptions.jniLibs.pickFirsts.addAll(
36-
listOf(
37-
// Hermes & JSC are provided by AAR dependencies we pre-bundle.
38-
"**/libhermes.so",
39-
"**/libjsc.so",
40-
// This is the .so provided by FBJNI via prefab
41-
"**/libfbjni.so",
42-
// Those are prefab libraries we distribute via ReactAndroid
43-
// Due to a bug in AGP, they fire a warning on console as both the JNI
44-
// and the prefab .so files gets considered. See more on:
45-
"**/libfabricjni.so",
46-
"**/libfolly_runtime.so",
47-
"**/libglog.so",
48-
"**/libjsi.so",
49-
"**/libreact_codegen_rncore.so",
50-
"**/libreact_debug.so",
51-
"**/libreact_nativemodule_core.so",
52-
"**/libreact_newarchdefaults.so",
53-
"**/libreact_render_componentregistry.so",
54-
"**/libreact_render_core.so",
55-
"**/libreact_render_debug.so",
56-
"**/libreact_render_graphics.so",
57-
"**/libreact_render_imagemanager.so",
58-
"**/libreact_render_mapbuffer.so",
59-
"**/librrc_image.so",
60-
"**/librrc_view.so",
61-
"**/libruntimeexecutor.so",
62-
"**/libturbomodulejsijni.so",
63-
"**/libyoga.so",
64-
// AGP will give priority of libc++_shared coming from App modules.
65-
"**/libc++_shared.so",
66-
))
23+
// For Old Arch, we don't need to setup the NDK
24+
return@finalizeDsl
25+
}
26+
// We enable prefab so users can consume .so/headers from ReactAndroid and hermes-engine
27+
// .aar
28+
ext.buildFeatures.prefab = true
6729

68-
// If the user has not provided a CmakeLists.txt path, let's provide
69-
// the default one from the framework
70-
if (ext.externalNativeBuild.cmake.path == null) {
71-
ext.externalNativeBuild.cmake.path =
72-
File(
73-
extension.reactNativeDir.get().asFile,
74-
"ReactAndroid/cmake-utils/default-app-setup/CMakeLists.txt")
75-
}
30+
// If the user has not provided a CmakeLists.txt path, let's provide
31+
// the default one from the framework
32+
if (ext.externalNativeBuild.cmake.path == null) {
33+
ext.externalNativeBuild.cmake.path =
34+
File(
35+
extension.reactNativeDir.get().asFile,
36+
"ReactAndroid/cmake-utils/default-app-setup/CMakeLists.txt")
37+
}
7638

77-
// Parameters should be provided in an additive manner (do not override what
78-
// the user provided, but allow for sensible defaults).
79-
val cmakeArgs = ext.defaultConfig.externalNativeBuild.cmake.arguments
80-
if ("-DGENERATED_SRC_DIR" !in cmakeArgs) {
81-
cmakeArgs.add("-DGENERATED_SRC_DIR=${File(project.buildDir, "generated/source")}")
82-
}
83-
if ("-DPROJECT_BUILD_DIR" !in cmakeArgs) {
84-
cmakeArgs.add("-DPROJECT_BUILD_DIR=${project.buildDir}")
85-
}
86-
if ("-DREACT_ANDROID_DIR" !in cmakeArgs) {
87-
cmakeArgs.add(
88-
"-DREACT_ANDROID_DIR=${extension.reactNativeDir.file("ReactAndroid").get().asFile}")
89-
}
90-
if ("-DREACT_ANDROID_BUILD_DIR" !in cmakeArgs) {
91-
cmakeArgs.add(
92-
"-DREACT_ANDROID_BUILD_DIR=${extension.reactNativeDir.file("ReactAndroid/build").get().asFile}")
93-
}
94-
if ("-DANDROID_STL" !in cmakeArgs) {
95-
cmakeArgs.add("-DANDROID_STL=c++_shared")
96-
}
39+
// Parameters should be provided in an additive manner (do not override what
40+
// the user provided, but allow for sensible defaults).
41+
val cmakeArgs = ext.defaultConfig.externalNativeBuild.cmake.arguments
42+
if ("-DGENERATED_SRC_DIR" !in cmakeArgs) {
43+
cmakeArgs.add("-DGENERATED_SRC_DIR=${File(project.buildDir, "generated/source")}")
44+
}
45+
if ("-DPROJECT_BUILD_DIR" !in cmakeArgs) {
46+
cmakeArgs.add("-DPROJECT_BUILD_DIR=${project.buildDir}")
47+
}
48+
if ("-DREACT_ANDROID_DIR" !in cmakeArgs) {
49+
cmakeArgs.add(
50+
"-DREACT_ANDROID_DIR=${extension.reactNativeDir.file("ReactAndroid").get().asFile}")
9751
}
52+
if ("-DREACT_ANDROID_BUILD_DIR" !in cmakeArgs) {
53+
cmakeArgs.add(
54+
"-DREACT_ANDROID_BUILD_DIR=${
55+
extension.reactNativeDir.file("ReactAndroid/build").get().asFile
56+
}")
57+
}
58+
if ("-DANDROID_STL" !in cmakeArgs) {
59+
cmakeArgs.add("-DANDROID_STL=c++_shared")
60+
}
61+
}
62+
}
63+
}
64+
65+
/**
66+
* This method is used to configure the .so Packaging Options for the given variant. It will make
67+
* sure we specify the correct .pickFirsts for all the .so files we are producing or that we're
68+
* aware of as some of our dependencies are pulling them in.
69+
*/
70+
fun configureNewArchPackagingOptions(
71+
project: Project,
72+
variant: Variant,
73+
) {
74+
if (!project.isNewArchEnabled) {
75+
// For Old Arch, we set a pickFirst only on libraries that we know are
76+
// clashing with our direct dependencies (FBJNI, Flipper and Hermes).
77+
variant.packaging.jniLibs.pickFirsts.addAll(
78+
listOf(
79+
"**/libfbjni.so",
80+
"**/libc++_shared.so",
81+
))
82+
} else {
83+
// We set some packagingOptions { pickFirst ... } for our users for libraries we own.
84+
variant.packaging.jniLibs.pickFirsts.addAll(
85+
listOf(
86+
// This is the .so provided by FBJNI via prefab
87+
"**/libfbjni.so",
88+
// Those are prefab libraries we distribute via ReactAndroid
89+
// Due to a bug in AGP, they fire a warning on console as both the JNI
90+
// and the prefab .so files gets considered. See more on:
91+
"**/libfabricjni.so",
92+
"**/libfolly_runtime.so",
93+
"**/libglog.so",
94+
"**/libjsi.so",
95+
"**/libreact_codegen_rncore.so",
96+
"**/libreact_debug.so",
97+
"**/libreact_nativemodule_core.so",
98+
"**/libreact_newarchdefaults.so",
99+
"**/libreact_render_componentregistry.so",
100+
"**/libreact_render_core.so",
101+
"**/libreact_render_debug.so",
102+
"**/libreact_render_graphics.so",
103+
"**/libreact_render_imagemanager.so",
104+
"**/libreact_render_mapbuffer.so",
105+
"**/librrc_image.so",
106+
"**/librrc_view.so",
107+
"**/libruntimeexecutor.so",
108+
"**/libturbomodulejsijni.so",
109+
"**/libyoga.so",
110+
// AGP will give priority of libc++_shared coming from App modules.
111+
"**/libc++_shared.so",
112+
))
113+
}
114+
}
115+
116+
/**
117+
* This method is used to configure the .so Cleanup for the given variant. It takes care of
118+
* cleaning up the .so files that are not needed for Hermes or JSC, given a specific variant.
119+
*/
120+
fun configureJsEnginePackagingOptions(
121+
config: ReactExtension,
122+
variant: Variant,
123+
hermesEnabled: Boolean,
124+
debuggableVariant: Boolean
125+
) {
126+
if (config.enableSoCleanup.get()) {
127+
val (excludes, includes) = getPackagingOptionsForVariant(hermesEnabled, debuggableVariant)
128+
variant.packaging.jniLibs.excludes.addAll(excludes)
129+
variant.packaging.jniLibs.pickFirsts.addAll(includes)
130+
}
131+
}
132+
133+
fun getPackagingOptionsForVariant(
134+
hermesEnabled: Boolean,
135+
debuggableVariant: Boolean
136+
): Pair<List<String>, List<String>> {
137+
val excludes = mutableListOf<String>()
138+
val includes = mutableListOf<String>()
139+
if (hermesEnabled) {
140+
excludes.add("**/libjsc.so")
141+
excludes.add("**/libjscexecutor.so")
142+
includes.add("**/libhermes.so")
143+
if (debuggableVariant) {
144+
excludes.add("**/libhermes-executor-release.so")
145+
includes.add("**/libhermes-executor-debug.so")
146+
} else {
147+
excludes.add("**/libhermes-executor-debug.so")
148+
includes.add("**/libhermes-executor-release.so")
98149
}
150+
} else {
151+
excludes.add("**/libhermes.so")
152+
excludes.add("**/libhermes-executor-debug.so")
153+
excludes.add("**/libhermes-executor-release.so")
154+
includes.add("**/libjsc.so")
155+
includes.add("**/libjscexecutor.so")
99156
}
157+
return excludes to includes
100158
}
101159
}

0 commit comments

Comments
 (0)