From 107ffa6ad98d92bbf4973651a2bbd641de04e702 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Ro=C5=BCniata?= <56474758+krozniata@users.noreply.github.com> Date: Tue, 9 Dec 2025 15:01:28 +0100 Subject: [PATCH 1/4] feat: use stripped native so libs --- .../brownfield/processors/JNILibsProcessor.kt | 60 ++++++++++++++----- 1 file changed, 45 insertions(+), 15 deletions(-) diff --git a/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/JNILibsProcessor.kt b/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/JNILibsProcessor.kt index 9da6057f..7fb86c3b 100644 --- a/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/JNILibsProcessor.kt +++ b/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/JNILibsProcessor.kt @@ -48,38 +48,41 @@ class JNILibsProcessor : BaseProject() { listOf("arm64-v8a", "armeabi-v7a", "x86_64", "x86") .associateWith { mutableListOf() } .toMutableMap() - for (archiveLibrary in aarLibraries) { val jniDir = archiveLibrary.getJniDir() processNestedLibs(jniDir.listFiles(), existingJNILibs) - if (jniDir.exists()) { - val filteredSourceSets = androidExtension.sourceSets.filter { sourceSet -> sourceSet.name == variant.name } - filteredSourceSets.forEach { sourceSet -> sourceSet.jniLibs.srcDir(jniDir) } - } + copyStrippedSoLibs(variant, existingJNILibs) } } } } - private fun copySoLibsTask(variant: LibraryVariant): TaskProvider { + private fun getStrippedLibsPath(variant: LibraryVariant): Pair { + val projectExt = project.extensions.getByType(Extension::class.java) + val appProject = project.rootProject.project(projectExt.appProjectName) + val appBuildDir = appProject.layout.buildDirectory.get() + val variantName = variant.name val capitalizedVariant = variantName.replaceFirstChar(Char::titlecase) + val fromDir = appBuildDir + .dir("intermediates/stripped_native_libs/$variantName/strip${capitalizedVariant}DebugSymbols/out/lib") + .asFile + + val intoDir = project.rootProject.file("${project.name}/libs$capitalizedVariant") + + return Pair(fromDir, intoDir) + } + + private fun copySoLibsTask(variant: LibraryVariant): TaskProvider { + val capitalizedVariant = variant.name.replaceFirstChar(Char::titlecase) val projectExt = project.extensions.getByType(Extension::class.java) val appProject = project.rootProject.project(projectExt.appProjectName) - val appBuildDir = appProject.layout.buildDirectory.get() val stripTask = ":${appProject.name}:strip${capitalizedVariant}DebugSymbols" val codegenTask = ":${project.name}:generateCodegenSchemaFromJavaScript" - val fromDir = - appBuildDir - .dir("intermediates/stripped_native_libs/$variantName/strip${capitalizedVariant}DebugSymbols/out/lib") - .asFile - - val intoDir = - project.rootProject - .file("${project.name}/libs$capitalizedVariant") + val (fromDir, intoDir) = getStrippedLibsPath(variant) return project.tasks.register("copy${capitalizedVariant}LibSources", Copy::class.java) { it.dependsOn(stripTask, codegenTask) @@ -91,6 +94,33 @@ class JNILibsProcessor : BaseProject() { } } + private fun copyStrippedSoLibs( + variant: LibraryVariant, + existingJNILibs: MutableMap> + ) { + val (fromDir, intoDir) = getStrippedLibsPath(variant) + + existingJNILibs.forEach { (arch, libNames) -> + val sourceArchDir = File(fromDir, arch) + if (!sourceArchDir.exists()) return@forEach + + val destArchDir = File(intoDir, arch).apply { mkdirs() } + + libNames.forEach { libName -> + val sourceFile = File(sourceArchDir, libName) + val destFile = File(destArchDir, libName) + + if (sourceFile.exists()) { + try { + sourceFile.copyTo(destFile, overwrite = true) + } catch (e: Exception) { + Logging.log("Failed to copy $libName: ${e.message}") + } + } + } + } + } + private fun processNestedLibs( files: Array?, existingJNILibs: MutableMap>, From 99d2ba071ca06a2c5002c791b7eb9a53756cf075 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Ro=C5=BCniata?= <56474758+krozniata@users.noreply.github.com> Date: Tue, 9 Dec 2025 15:16:27 +0100 Subject: [PATCH 2/4] fix: detekt --- .../brownfield/processors/JNILibsProcessor.kt | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/JNILibsProcessor.kt b/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/JNILibsProcessor.kt index 7fb86c3b..56fea165 100644 --- a/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/JNILibsProcessor.kt +++ b/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/JNILibsProcessor.kt @@ -51,7 +51,7 @@ class JNILibsProcessor : BaseProject() { for (archiveLibrary in aarLibraries) { val jniDir = archiveLibrary.getJniDir() processNestedLibs(jniDir.listFiles(), existingJNILibs) - copyStrippedSoLibs(variant, existingJNILibs) + copyStrippedSoLibs(variant, existingJNILibs) } } } @@ -65,12 +65,13 @@ class JNILibsProcessor : BaseProject() { val variantName = variant.name val capitalizedVariant = variantName.replaceFirstChar(Char::titlecase) - val fromDir = appBuildDir - .dir("intermediates/stripped_native_libs/$variantName/strip${capitalizedVariant}DebugSymbols/out/lib") - .asFile + val fromDir = + appBuildDir + .dir("intermediates/stripped_native_libs/$variantName/strip${capitalizedVariant}DebugSymbols/out/lib") + .asFile val intoDir = project.rootProject.file("${project.name}/libs$capitalizedVariant") - + return Pair(fromDir, intoDir) } @@ -96,20 +97,20 @@ class JNILibsProcessor : BaseProject() { private fun copyStrippedSoLibs( variant: LibraryVariant, - existingJNILibs: MutableMap> + existingJNILibs: MutableMap>, ) { val (fromDir, intoDir) = getStrippedLibsPath(variant) - + existingJNILibs.forEach { (arch, libNames) -> val sourceArchDir = File(fromDir, arch) if (!sourceArchDir.exists()) return@forEach - + val destArchDir = File(intoDir, arch).apply { mkdirs() } - + libNames.forEach { libName -> val sourceFile = File(sourceArchDir, libName) val destFile = File(destArchDir, libName) - + if (sourceFile.exists()) { try { sourceFile.copyTo(destFile, overwrite = true) From 81c7ec0c26f7c688843fdccc551376e2d87cf622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Ro=C5=BCniata?= <56474758+krozniata@users.noreply.github.com> Date: Tue, 9 Dec 2025 16:34:30 +0100 Subject: [PATCH 3/4] fix: detekt --- .../brownfield/processors/JNILibsProcessor.kt | 45 ++++++++++++------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/JNILibsProcessor.kt b/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/JNILibsProcessor.kt index 56fea165..2f759399 100644 --- a/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/JNILibsProcessor.kt +++ b/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/JNILibsProcessor.kt @@ -10,7 +10,6 @@ package com.callstack.react.brownfield.processors -import com.android.build.gradle.LibraryExtension import com.android.build.gradle.api.LibraryVariant import com.callstack.react.brownfield.exceptions.TaskNotFound import com.callstack.react.brownfield.shared.BaseProject @@ -36,7 +35,6 @@ class JNILibsProcessor : BaseProject() { throw TaskNotFound("Task $taskName not found") } - val androidExtension = project.extensions.getByName("android") as LibraryExtension val copyTask = copySoLibsTask(variant) mergeJniLibsTask.configure { @@ -102,22 +100,39 @@ class JNILibsProcessor : BaseProject() { val (fromDir, intoDir) = getStrippedLibsPath(variant) existingJNILibs.forEach { (arch, libNames) -> - val sourceArchDir = File(fromDir, arch) - if (!sourceArchDir.exists()) return@forEach + copyLibsForArchitecture(fromDir, intoDir, arch, libNames) + } + } - val destArchDir = File(intoDir, arch).apply { mkdirs() } + private fun copyLibsForArchitecture( + fromDir: File, + intoDir: File, + arch: String, + libNames: List, + ) { + val sourceArchDir = File(fromDir, arch) + if (!sourceArchDir.exists()) return - libNames.forEach { libName -> - val sourceFile = File(sourceArchDir, libName) - val destFile = File(destArchDir, libName) + val destArchDir = File(intoDir, arch).apply { mkdirs() } - if (sourceFile.exists()) { - try { - sourceFile.copyTo(destFile, overwrite = true) - } catch (e: Exception) { - Logging.log("Failed to copy $libName: ${e.message}") - } - } + libNames.forEach { libName -> + copyLibFile(sourceArchDir, destArchDir, libName) + } + } + + private fun copyLibFile( + sourceArchDir: File, + destArchDir: File, + libName: String, + ) { + val sourceFile = File(sourceArchDir, libName) + val destFile = File(destArchDir, libName) + + if (sourceFile.exists()) { + try { + sourceFile.copyTo(destFile, overwrite = true) + } catch (e: java.io.IOException) { + Logging.log("Failed to copy $libName: ${e.message}") } } } From d4c2111f5098c7df836376cffd416c067b307f9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Ro=C5=BCniata?= <56474758+krozniata@users.noreply.github.com> Date: Mon, 22 Dec 2025 14:32:57 +0100 Subject: [PATCH 4/4] chore: hide behind flag --- .../react/brownfield/processors/JNILibsProcessor.kt | 12 +++++++++++- .../callstack/react/brownfield/utils/Extension.kt | 7 +++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/JNILibsProcessor.kt b/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/JNILibsProcessor.kt index 2f759399..5b59cc8d 100644 --- a/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/JNILibsProcessor.kt +++ b/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/JNILibsProcessor.kt @@ -10,6 +10,7 @@ package com.callstack.react.brownfield.processors +import com.android.build.gradle.LibraryExtension import com.android.build.gradle.api.LibraryVariant import com.callstack.react.brownfield.exceptions.TaskNotFound import com.callstack.react.brownfield.shared.BaseProject @@ -35,6 +36,7 @@ class JNILibsProcessor : BaseProject() { throw TaskNotFound("Task $taskName not found") } + val androidExtension = project.extensions.getByName("android") as LibraryExtension val copyTask = copySoLibsTask(variant) mergeJniLibsTask.configure { @@ -42,6 +44,7 @@ class JNILibsProcessor : BaseProject() { it.dependsOn(copyTask) it.doFirst { + val projectExt = project.extensions.getByType(Extension::class.java) val existingJNILibs = listOf("arm64-v8a", "armeabi-v7a", "x86_64", "x86") .associateWith { mutableListOf() } @@ -49,7 +52,14 @@ class JNILibsProcessor : BaseProject() { for (archiveLibrary in aarLibraries) { val jniDir = archiveLibrary.getJniDir() processNestedLibs(jniDir.listFiles(), existingJNILibs) - copyStrippedSoLibs(variant, existingJNILibs) + if (projectExt.experimentalUseStrippedSoFiles) { + copyStrippedSoLibs(variant, existingJNILibs) + } else { + if (jniDir.exists()) { + val filteredSourceSets = androidExtension.sourceSets.filter { sourceSet -> sourceSet.name == variant.name } + filteredSourceSets.forEach { sourceSet -> sourceSet.jniLibs.srcDir(jniDir) } + } + } } } } diff --git a/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/utils/Extension.kt b/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/utils/Extension.kt index 76b69567..130c6545 100644 --- a/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/utils/Extension.kt +++ b/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/utils/Extension.kt @@ -45,4 +45,11 @@ open class Extension { * bundled. */ var dynamicLibs = listOf() + + /** + * Whether to use stripped .so files. + * + * Default value is `false` + */ + var experimentalUseStrippedSoFiles = false }