@callstack/react-native-brownfield 3.11.0 → 3.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/gradle-plugin/brownfield/build.gradle.kts +134 -0
  3. package/gradle-plugin/brownfield/config/detekt/detekt.yml +936 -0
  4. package/gradle-plugin/brownfield/gradle/libs.versions.toml +19 -0
  5. package/gradle-plugin/brownfield/gradle/wrapper/gradle-wrapper.jar +0 -0
  6. package/gradle-plugin/brownfield/gradle/wrapper/gradle-wrapper.properties +7 -0
  7. package/gradle-plugin/brownfield/gradle.properties +14 -0
  8. package/gradle-plugin/brownfield/gradlew +249 -0
  9. package/gradle-plugin/brownfield/gradlew.bat +92 -0
  10. package/gradle-plugin/brownfield/settings.gradle +7 -0
  11. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/artifacts/ArtifactsResolver.kt +132 -0
  12. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/exceptions/NameSpaceNotFound.kt +4 -0
  13. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/exceptions/TaskNotFound.kt +4 -0
  14. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/expo/ExpoPublishingHelper.kt +432 -0
  15. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/expo/utils/BrownfieldPrimitives.kt +31 -0
  16. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/expo/utils/ExpoProjectionPrimitives.kt +22 -0
  17. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/expo/utils/LocalMavenUtils.kt +83 -0
  18. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/expo/utils/ReflectionUtils.kt +79 -0
  19. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/expo/utils/VersionMediatingDependencySet.kt +100 -0
  20. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/plugin/CustomDependencyResolver.kt +50 -0
  21. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/plugin/ProjectConfigurations.kt +107 -0
  22. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/plugin/RClassTransformer.kt +131 -0
  23. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/plugin/RNBrownfieldPlugin.kt +171 -0
  24. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/plugin/RNSourceSets.kt +149 -0
  25. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/AssetTaskProcessor.kt +29 -0
  26. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/ExplodeTaskProvider.kt +63 -0
  27. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/JNILibsProcessor.kt +165 -0
  28. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/ManifestTaskProcessor.kt +74 -0
  29. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/MergeProcessor.kt +55 -0
  30. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/ProguardProcessor.kt +61 -0
  31. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/ResourceTaskProcessor.kt +26 -0
  32. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/VariantHelper.kt +61 -0
  33. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/VariantPackagesProperty.kt +21 -0
  34. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/VariantTaskProvider.kt +103 -0
  35. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/BaseProject.kt +17 -0
  36. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/BundleTaskProvider.kt +46 -0
  37. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/Constants.kt +58 -0
  38. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/ExplodeAarTask.kt +72 -0
  39. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/GradleILogger.kt +59 -0
  40. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/Logging.kt +24 -0
  41. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/ModuleInfo.kt +22 -0
  42. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/utils/AndroidArchiveLibrary.kt +77 -0
  43. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/utils/DirectoryManager.kt +20 -0
  44. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/utils/Extension.kt +44 -0
  45. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/utils/StringExtensions.kt +5 -0
  46. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/utils/StringMatcher.kt +21 -0
  47. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/utils/Utils.kt +59 -0
  48. package/ios/Expo/ExpoHostRuntime.swift +3 -2
  49. package/ios/ReactNativeBrownfield.swift +1 -1
  50. package/ios/Vanilla/ReactNativeHostRuntime.swift +5 -2
  51. package/ios/swiftpm/Sources/BrownfieldBundleSupport/BrownfieldBundleURLResolver.swift +11 -5
  52. package/ios/swiftpm/Tests/BrownfieldBundleSupportTests/BrownfieldBundleURLResolverTests.swift +17 -5
  53. package/lib/commonjs/expo-config-plugin/android/utils/constants.js +1 -1
  54. package/lib/commonjs/expo-config-plugin/android/utils/constants.js.map +1 -1
  55. package/lib/commonjs/expo-config-plugin/ios/xcodeHelpers.js +2 -1
  56. package/lib/commonjs/expo-config-plugin/ios/xcodeHelpers.js.map +1 -1
  57. package/lib/commonjs/index.js.map +1 -1
  58. package/lib/module/expo-config-plugin/android/utils/constants.js +1 -1
  59. package/lib/module/expo-config-plugin/android/utils/constants.js.map +1 -1
  60. package/lib/module/expo-config-plugin/ios/xcodeHelpers.js +2 -1
  61. package/lib/module/expo-config-plugin/ios/xcodeHelpers.js.map +1 -1
  62. package/lib/module/index.js.map +1 -1
  63. package/lib/typescript/commonjs/src/expo-config-plugin/android/utils/constants.d.ts +2 -2
  64. package/lib/typescript/commonjs/src/expo-config-plugin/android/utils/constants.d.ts.map +1 -1
  65. package/lib/typescript/commonjs/src/expo-config-plugin/ios/xcodeHelpers.d.ts.map +1 -1
  66. package/lib/typescript/commonjs/src/index.d.ts +1 -0
  67. package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
  68. package/lib/typescript/module/src/expo-config-plugin/android/utils/constants.d.ts +2 -2
  69. package/lib/typescript/module/src/expo-config-plugin/android/utils/constants.d.ts.map +1 -1
  70. package/lib/typescript/module/src/expo-config-plugin/ios/xcodeHelpers.d.ts.map +1 -1
  71. package/lib/typescript/module/src/index.d.ts +1 -0
  72. package/lib/typescript/module/src/index.d.ts.map +1 -1
  73. package/package.json +6 -4
  74. package/scripts/sync-gradle-plugin-source.js +58 -0
  75. package/src/expo-config-plugin/android/utils/constants.ts +1 -1
  76. package/src/expo-config-plugin/ios/xcodeHelpers.ts +14 -0
  77. package/src/index.ts +2 -0
@@ -0,0 +1,149 @@
1
+ package com.callstack.react.brownfield.plugin
2
+
3
+ import com.android.build.api.variant.LibraryAndroidComponentsExtension
4
+ import com.android.build.gradle.LibraryExtension
5
+ import com.callstack.react.brownfield.exceptions.NameSpaceNotFound
6
+ import com.callstack.react.brownfield.utils.Extension
7
+ import com.callstack.react.brownfield.utils.Utils
8
+ import com.callstack.react.brownfield.utils.capitalized
9
+ import org.gradle.api.Project
10
+ import org.gradle.api.Task
11
+ import org.gradle.api.tasks.Copy
12
+ import java.io.File
13
+
14
+ object RNSourceSets {
15
+ private lateinit var project: Project
16
+ private lateinit var extension: Extension
17
+ private lateinit var androidExtension: LibraryExtension
18
+
19
+ fun configure(
20
+ project: Project,
21
+ extension: Extension,
22
+ ) {
23
+ this.project = project
24
+ this.extension = extension
25
+
26
+ androidExtension = this.project.extensions.getByType(LibraryExtension::class.java)
27
+
28
+ configureSourceSets()
29
+ configureTasks()
30
+ }
31
+
32
+ private fun getAppProject(): Project = project.rootProject.project(extension.appProjectName)
33
+
34
+ private fun getAppBuildDir() = getAppProject().layout.buildDirectory.get()
35
+
36
+ private fun getModuleBuildDir() = project.layout.buildDirectory.get()
37
+
38
+ private fun configureSourceSets() {
39
+ // 1. Get the 'androidComponents' extension for the new Variant API
40
+ val componentsExtension = project.extensions.getByType(LibraryAndroidComponentsExtension::class.java)
41
+
42
+ // Move the non-variant-specific configuration out of the loop
43
+ androidExtension.sourceSets.named("main") { sourceSet ->
44
+ // This path is not variant-specific, so it's added once here.
45
+ sourceSet.java.srcDir("${getModuleBuildDir()}/generated/autolinking/src/main/java")
46
+ }
47
+
48
+ // 2. Use the onVariants block to configure each variant
49
+ componentsExtension.onVariants { variant ->
50
+ val variantName = variant.name
51
+ val bundledAssetsVariantName =
52
+ Utils.getBundledAssetsVariantName(
53
+ variantName = variantName,
54
+ buildTypeName = variant.buildType,
55
+ isDebuggable = variant.debuggable,
56
+ )
57
+ val capitalizedBundledAssetsVariantName = bundledAssetsVariantName.capitalized()
58
+
59
+ // 3. Lazily configure the 'variant-specific' source set using .named()
60
+ androidExtension.sourceSets.named(variantName) { sourceSet ->
61
+ // Paths are collected and added, similar to your improved version
62
+ val bundlePathSegments =
63
+ listOf(
64
+ // outputs for RN <= 0.81
65
+ "createBundle${capitalizedBundledAssetsVariantName}JsAndAssets",
66
+ // outputs for RN >= 0.82
67
+ "react/$bundledAssetsVariantName",
68
+ // expo update resources
69
+ "create${capitalizedBundledAssetsVariantName}UpdatesResources",
70
+ )
71
+
72
+ // Add the variant-specific generated asset and resource directories
73
+ val appBuildDir = getAppBuildDir()
74
+ sourceSet.assets.srcDirs(bundlePathSegments.map { "$appBuildDir/generated/assets/$it" })
75
+ sourceSet.res.srcDirs(bundlePathSegments.map { "$appBuildDir/generated/res/$it" })
76
+ sourceSet.jniLibs.srcDirs("libs${variantName.capitalized()}")
77
+ }
78
+ }
79
+ }
80
+
81
+ private fun getLibraryNameSpace(): String {
82
+ val nameSpace = androidExtension.namespace
83
+ return nameSpace
84
+ ?: throw NameSpaceNotFound("namespace must be defined in your android library build.gradle")
85
+ }
86
+
87
+ private fun patchRNEntryPoint(
88
+ task: Task,
89
+ path: String,
90
+ ) {
91
+ val rnEntryPointTaskName = "generateReactNativeEntryPoint"
92
+
93
+ /**
94
+ * If `generateReactNativeEntryPoint` task does not exist, we early return. It means
95
+ * the consumer library is running on RN version < 0.80
96
+ */
97
+ val rnEntryPointTask = getAppProject().tasks.findByName(rnEntryPointTaskName) ?: return
98
+
99
+ task.dependsOn(rnEntryPointTask)
100
+ val sourceFile =
101
+ File(
102
+ getModuleBuildDir().toString(),
103
+ "$path/com/facebook/react/ReactNativeApplicationEntryPoint.java",
104
+ )
105
+ task.doLast {
106
+ if (sourceFile.exists()) {
107
+ var content = sourceFile.readText()
108
+ val nameSpace = getLibraryNameSpace()
109
+
110
+ /**
111
+ * We use look-ahead regex to replace any occurrences with Build.Config referenced via the old(app) package
112
+ *
113
+ * \b[\w.]+ → matches the old package
114
+ * (?=\.BuildConfig) → only if it’s immediately followed by that suffix
115
+ */
116
+ val regex = Regex("""\b[\w.]+(?=\.BuildConfig)""")
117
+ content = content.replace(regex, nameSpace)
118
+ sourceFile.writeText(content)
119
+ }
120
+ }
121
+ }
122
+
123
+ private fun configureTasks() {
124
+ project.tasks.register("copyAutolinkingSources", Copy::class.java) {
125
+ val path = "generated/autolinking/src/main/java"
126
+ val appBuildDir = getAppBuildDir()
127
+ it.dependsOn(":${getAppProject().name}:generateAutolinkingPackageList")
128
+ it.from("$appBuildDir/$path")
129
+ it.into("${getModuleBuildDir()}/$path")
130
+
131
+ patchRNEntryPoint(it, path)
132
+ }
133
+
134
+ project.tasks.named("preBuild").configure {
135
+ /**
136
+ * Ensure auto-generated sources are available before compilation.
137
+ *
138
+ * This hooks into the global `preBuild` task, so the dependency runs
139
+ * regardless of build variant. Use this only when the generated sources
140
+ * are identical for all variants (debug/release).
141
+ *
142
+ * If variant-specific
143
+ * files are needed, prefer `preDebugBuild` / `preReleaseBuild` under
144
+ * `VariantProcessor.processVariant`
145
+ */
146
+ it.dependsOn("copyAutolinkingSources")
147
+ }
148
+ }
149
+ }
@@ -0,0 +1,29 @@
1
+ package com.callstack.react.brownfield.processors
2
+
3
+ import com.android.build.gradle.LibraryExtension
4
+ import com.android.build.gradle.api.LibraryVariant
5
+ import com.callstack.react.brownfield.utils.AndroidArchiveLibrary
6
+ import org.gradle.api.Project
7
+
8
+ object AssetTaskProcessor {
9
+ fun process(
10
+ variant: LibraryVariant,
11
+ project: Project,
12
+ aarLibraries: List<AndroidArchiveLibrary>,
13
+ ) {
14
+ val assetsTask = variant.mergeAssetsProvider.get()
15
+
16
+ val androidExtension = project.extensions.getByName("android") as LibraryExtension
17
+ assetsTask.doFirst {
18
+ val filteredSourceSets =
19
+ androidExtension.sourceSets.filter { it.name == variant.name }
20
+
21
+ filteredSourceSets.forEach { sourceSet ->
22
+ val filteredAarLibs = aarLibraries.filter { it.getAssetsDir().exists() }
23
+ if (!filteredAarLibs.isEmpty()) {
24
+ sourceSet.assets.srcDirs(filteredAarLibs.map { it.getAssetsDir() })
25
+ }
26
+ }
27
+ }
28
+ }
29
+ }
@@ -0,0 +1,63 @@
1
+ package com.callstack.react.brownfield.processors
2
+
3
+ import com.android.build.gradle.api.LibraryVariant
4
+ import com.callstack.react.brownfield.shared.BundleTaskProvider
5
+ import com.callstack.react.brownfield.shared.ExplodeAarTask
6
+ import com.callstack.react.brownfield.shared.UnresolvedArtifactInfo
7
+ import com.callstack.react.brownfield.utils.capitalized
8
+ import org.gradle.api.Project
9
+ import org.gradle.api.Task
10
+ import org.gradle.api.tasks.TaskProvider
11
+ import org.gradle.api.tasks.bundling.Zip
12
+ import java.io.File
13
+
14
+ object ExplodeTaskProvider {
15
+ fun getTask(
16
+ variant: LibraryVariant,
17
+ project: Project,
18
+ artifacts: List<UnresolvedArtifactInfo>,
19
+ ): TaskProvider<ExplodeAarTask> {
20
+ val variantTaskProvider = VariantTaskProvider(project)
21
+ val bundleProvider = BundleTaskProvider(variantTaskProvider)
22
+ val capitalizedVariantName = variant.name.capitalized()
23
+
24
+ return project.tasks.register(
25
+ "explode${capitalizedVariantName}Aar",
26
+ ExplodeAarTask::class.java,
27
+ ) { task ->
28
+ task.variantName.set(variant.name)
29
+ task.minifyEnabled.set(variant.buildType.isMinifyEnabled)
30
+
31
+ val finalArtifacts = mutableListOf<UnresolvedArtifactInfo>()
32
+ artifacts.forEach { art ->
33
+ var artifactPath = art.file
34
+ if (art.isExpoPublishDependency != true) {
35
+ val defaultTaskName = "bundle${capitalizedVariantName}Aar"
36
+ val dependencyProject = project.project(":${art.moduleName}")
37
+ val bundleTaskProvider = bundleProvider.getBundleTask(dependencyProject, variant)
38
+ val taskName = bundleTaskProvider?.name ?: defaultTaskName
39
+
40
+ dependencyProject.tasks.findByName(taskName)?.let { task.dependsOn(it) }
41
+ artifactPath = createArtifactFile(bundleTaskProvider?.get() as Task).absolutePath
42
+ }
43
+
44
+ finalArtifacts.add(
45
+ UnresolvedArtifactInfo(
46
+ art.moduleGroup,
47
+ art.moduleName,
48
+ art.moduleVersion,
49
+ artifactPath,
50
+ isExpoPublishDependency = art.isExpoPublishDependency,
51
+ ),
52
+ )
53
+ }
54
+
55
+ task.inputArtifacts.set(finalArtifacts)
56
+ }
57
+ }
58
+
59
+ private fun createArtifactFile(bundle: Task): File {
60
+ val packageLibraryProvider = bundle as Zip
61
+ return File(packageLibraryProvider.destinationDirectory.get().asFile, packageLibraryProvider.archiveFileName.get())
62
+ }
63
+ }
@@ -0,0 +1,165 @@
1
+ package com.callstack.react.brownfield.processors
2
+
3
+ import com.android.build.gradle.LibraryExtension
4
+ import com.callstack.react.brownfield.exceptions.TaskNotFound
5
+ import com.callstack.react.brownfield.shared.Logging
6
+ import com.callstack.react.brownfield.utils.AndroidArchiveLibrary
7
+ import com.callstack.react.brownfield.utils.Extension
8
+ import com.callstack.react.brownfield.utils.capitalized
9
+ import org.gradle.api.Project
10
+ import org.gradle.api.tasks.Copy
11
+ import org.gradle.api.tasks.TaskProvider
12
+ import java.io.File
13
+
14
+ class JNILibsProcessor(val project: Project) {
15
+ fun processJniLibs(
16
+ aarLibraries: Collection<AndroidArchiveLibrary>,
17
+ variantName: String,
18
+ ) {
19
+ val capitalizedVariantName = variantName.capitalized()
20
+ val taskName = "merge${capitalizedVariantName}JniLibFolders"
21
+ val mergeJniLibsTask = project.tasks.named(taskName)
22
+
23
+ if (!mergeJniLibsTask.isPresent) {
24
+ throw TaskNotFound("Task $taskName not found")
25
+ }
26
+
27
+ val androidExtension = project.extensions.getByName("android") as LibraryExtension
28
+ val copyTask = copySoLibsTask(variantName)
29
+
30
+ mergeJniLibsTask.configure {
31
+ it.dependsOn(copyTask)
32
+
33
+ it.doFirst {
34
+ val projectExt = project.extensions.getByType(Extension::class.java)
35
+ val existingJNILibs =
36
+ listOf("arm64-v8a", "armeabi-v7a", "x86_64", "x86")
37
+ .associateWith { mutableListOf<String>() }
38
+ .toMutableMap()
39
+ for (archiveLibrary in aarLibraries) {
40
+ val jniDir = archiveLibrary.getJniDir()
41
+ processNestedLibs(jniDir.listFiles(), existingJNILibs)
42
+ if (!projectExt.useStrippedSoFiles) {
43
+ if (jniDir.exists()) {
44
+ val filteredSourceSets =
45
+ androidExtension.sourceSets.filter { sourceSet -> sourceSet.name == variantName }
46
+ filteredSourceSets.forEach { sourceSet ->
47
+ sourceSet.jniLibs.srcDir(
48
+ jniDir,
49
+ )
50
+ }
51
+ }
52
+ }
53
+ }
54
+ if (projectExt.useStrippedSoFiles) {
55
+ copyStrippedSoLibs(variantName, existingJNILibs)
56
+ }
57
+ }
58
+ }
59
+ }
60
+
61
+ private fun getStrippedLibsPath(variantName: String): Pair<File, File> {
62
+ val projectExt = project.extensions.getByType(Extension::class.java)
63
+ val appProject = project.rootProject.project(projectExt.appProjectName)
64
+ val appBuildDir = appProject.layout.buildDirectory.get()
65
+
66
+ val capitalizedVariant = variantName.capitalized()
67
+
68
+ val fromDir =
69
+ appBuildDir
70
+ .dir("intermediates/stripped_native_libs/$variantName/strip${capitalizedVariant}DebugSymbols/out/lib")
71
+ .asFile
72
+
73
+ val intoDir = project.rootProject.file("${project.name}/libs$capitalizedVariant")
74
+
75
+ return Pair(fromDir, intoDir)
76
+ }
77
+
78
+ private fun copySoLibsTask(variantName: String): TaskProvider<Copy> {
79
+ val capitalizedVariant = variantName.capitalized()
80
+ val projectExt = project.extensions.getByType(Extension::class.java)
81
+ val appProject = project.rootProject.project(projectExt.appProjectName)
82
+
83
+ val stripTask = ":${appProject.name}:strip${capitalizedVariant}DebugSymbols"
84
+ val codegenTask = ":${project.name}:generateCodegenSchemaFromJavaScript"
85
+
86
+ val (fromDir, intoDir) = getStrippedLibsPath(variantName)
87
+
88
+ return project.tasks.register("copy${capitalizedVariant}LibSources", Copy::class.java) {
89
+ it.dependsOn(stripTask, codegenTask)
90
+ it.from(fromDir)
91
+ it.into(intoDir)
92
+
93
+ it.include("**/libappmodules.so", "**/libreact_codegen_*.so")
94
+ projectExt.dynamicLibs.forEach { lib -> it.include("**/$lib") }
95
+ }
96
+ }
97
+
98
+ private fun copyStrippedSoLibs(
99
+ variantName: String,
100
+ existingJNILibs: MutableMap<String, MutableList<String>>,
101
+ ) {
102
+ val (fromDir, intoDir) = getStrippedLibsPath(variantName)
103
+
104
+ existingJNILibs.forEach { (arch, libNames) ->
105
+ copyLibsForArchitecture(fromDir, intoDir, arch, libNames)
106
+ }
107
+ }
108
+
109
+ private fun copyLibsForArchitecture(
110
+ fromDir: File,
111
+ intoDir: File,
112
+ arch: String,
113
+ libNames: List<String>,
114
+ ) {
115
+ val sourceArchDir = File(fromDir, arch)
116
+ if (!sourceArchDir.exists()) return
117
+
118
+ val destArchDir = File(intoDir, arch).apply { mkdirs() }
119
+
120
+ libNames.forEach { libName ->
121
+ copyLibFile(sourceArchDir, destArchDir, libName)
122
+ }
123
+ }
124
+
125
+ private fun copyLibFile(
126
+ sourceArchDir: File,
127
+ destArchDir: File,
128
+ libName: String,
129
+ ) {
130
+ val sourceFile = File(sourceArchDir, libName)
131
+ val destFile = File(destArchDir, libName)
132
+
133
+ if (sourceFile.exists()) {
134
+ try {
135
+ sourceFile.copyTo(destFile, overwrite = true)
136
+ } catch (e: java.io.IOException) {
137
+ Logging.log("Failed to copy $libName: ${e.message}")
138
+ }
139
+ }
140
+ }
141
+
142
+ private fun processNestedLibs(
143
+ files: Array<File>?,
144
+ existingJNILibs: MutableMap<String, MutableList<String>>,
145
+ ) {
146
+ files?.forEach { folder ->
147
+ val libFiles = folder.listFiles() ?: return@forEach
148
+ val libList = existingJNILibs[folder.name] ?: return@forEach
149
+
150
+ libFiles.forEach { file ->
151
+ if (file.name in libList) {
152
+ deleteFile(file)
153
+ } else {
154
+ libList.add(file.name)
155
+ }
156
+ }
157
+ }
158
+ }
159
+
160
+ private fun deleteFile(file: File) {
161
+ if (!file.delete()) {
162
+ Logging.log("Failed to delete: ${file.name}")
163
+ }
164
+ }
165
+ }
@@ -0,0 +1,74 @@
1
+ package com.callstack.react.brownfield.processors
2
+
3
+ import com.android.build.gradle.api.LibraryVariant
4
+ import com.android.manifmerger.ManifestMerger2
5
+ import com.android.manifmerger.ManifestProvider
6
+ import com.android.manifmerger.MergingReport
7
+ import com.android.utils.ILogger
8
+ import com.callstack.react.brownfield.shared.GradleILogger
9
+ import com.callstack.react.brownfield.utils.AndroidArchiveLibrary
10
+ import com.callstack.react.brownfield.utils.capitalized
11
+ import org.apache.tools.ant.BuildException
12
+ import org.gradle.api.Project
13
+ import org.gradle.api.logging.Logger
14
+ import java.io.BufferedWriter
15
+ import java.io.File
16
+ import java.io.FileOutputStream
17
+ import java.io.OutputStreamWriter
18
+
19
+ object ManifestTaskProcessor {
20
+ fun process(
21
+ variant: LibraryVariant,
22
+ project: Project,
23
+ aarLibraries: List<AndroidArchiveLibrary>,
24
+ ) {
25
+ val processManifestTask = variant.outputs.first().processManifestProvider.get()
26
+ val variantName = variant.name
27
+ processManifestTask.doLast {
28
+ val buildDir = project.layout.buildDirectory.get()
29
+ val manifestOutput =
30
+ project.file(
31
+ "$buildDir/intermediates/merged_manifest/$variantName/process${variantName.capitalized()}Manifest/AndroidManifest.xml",
32
+ )
33
+
34
+ val inputManifests = aarLibraries.map { it.getManifestFile() }
35
+ mergeManifests(manifestOutput, inputManifests, manifestOutput, project.logger)
36
+ }
37
+ }
38
+
39
+ private fun mergeManifests(
40
+ mainManifestFile: File,
41
+ secondaryManifestFiles: List<File>,
42
+ outputFile: File,
43
+ logger: Logger,
44
+ ) {
45
+ val iLogger: ILogger = GradleILogger(logger)
46
+ val mergerInvoker = ManifestMerger2.newMerger(mainManifestFile, iLogger, ManifestMerger2.MergeType.LIBRARY)
47
+ val manifestProviders = mutableListOf<ManifestProvider>()
48
+
49
+ val filteredSecondaryManifests = secondaryManifestFiles.filter { it.exists() }
50
+ filteredSecondaryManifests.forEach { file ->
51
+ manifestProviders.add(
52
+ object : ManifestProvider {
53
+ override fun getManifest(): File = file.absoluteFile
54
+
55
+ override fun getName(): String = file.name
56
+ },
57
+ )
58
+ }
59
+
60
+ mergerInvoker.addManifestProviders(manifestProviders)
61
+ val mergingReport: MergingReport = mergerInvoker.merge()
62
+
63
+ if (mergingReport.result.isError) {
64
+ logger.error(mergingReport.reportString)
65
+ mergingReport.log(iLogger)
66
+ throw BuildException(mergingReport.reportString)
67
+ }
68
+
69
+ BufferedWriter(OutputStreamWriter(FileOutputStream(outputFile), "UTF-8")).use { writer ->
70
+ writer.append(mergingReport.getMergedDocument(MergingReport.MergedManifestKind.MERGED))
71
+ writer.flush()
72
+ }
73
+ }
74
+ }
@@ -0,0 +1,55 @@
1
+ package com.callstack.react.brownfield.processors
2
+
3
+ import com.callstack.react.brownfield.utils.AndroidArchiveLibrary
4
+ import org.gradle.api.Project
5
+ import java.io.File
6
+
7
+ object MergeProcessor {
8
+ fun mergeClassesJarIntoClasses(
9
+ project: Project,
10
+ androidLibraries: Collection<AndroidArchiveLibrary>,
11
+ folderOut: File,
12
+ ) {
13
+ val allJarFiles: MutableCollection<File> = ArrayList()
14
+ val filteredLibs = getFilteredAarLibs(androidLibraries)
15
+ filteredLibs.forEach {
16
+ allJarFiles.add(it.getClassesJarFile())
17
+ }
18
+
19
+ val filteredAllJarFiles = allJarFiles.filter { it.exists() }
20
+ filteredAllJarFiles.forEach { jarFile ->
21
+ project.copy {
22
+ it.from(project.zipTree(jarFile))
23
+ it.into(folderOut)
24
+ }
25
+ }
26
+ }
27
+
28
+ fun mergeLibsIntoClasses(
29
+ project: Project,
30
+ androidLibraries: Collection<AndroidArchiveLibrary>,
31
+ jarFiles: Collection<File>,
32
+ outputDir: File,
33
+ ) {
34
+ val allJarFiles: MutableCollection<File> = ArrayList()
35
+ val filteredLibs = getFilteredAarLibs(androidLibraries)
36
+ filteredLibs.forEach {
37
+ allJarFiles.addAll(it.getLocalJars())
38
+ }
39
+
40
+ val filteredJarFiles = jarFiles.filter { it.exists() }
41
+ filteredJarFiles.forEach { allJarFiles.add(it) }
42
+
43
+ allJarFiles.forEach { jarFile ->
44
+ project.copy {
45
+ it.from(project.zipTree(jarFile))
46
+ it.into(outputDir)
47
+ it.exclude("META-INF/")
48
+ }
49
+ }
50
+ }
51
+
52
+ private fun getFilteredAarLibs(androidLibraries: Collection<AndroidArchiveLibrary>): Collection<AndroidArchiveLibrary> {
53
+ return androidLibraries.filter { it.getExplodedAarRootDir().exists() }
54
+ }
55
+ }
@@ -0,0 +1,61 @@
1
+ package com.callstack.react.brownfield.processors
2
+
3
+ import com.callstack.react.brownfield.exceptions.TaskNotFound
4
+ import com.callstack.react.brownfield.shared.Logging
5
+ import com.callstack.react.brownfield.utils.Utils
6
+ import org.gradle.api.Project
7
+ import org.gradle.api.file.RegularFileProperty
8
+ import org.gradle.api.tasks.TaskProvider
9
+ import java.io.File
10
+
11
+ class ProguardProcessor(val project: Project) {
12
+ fun processConsumerFiles(
13
+ proguardRules: List<File>,
14
+ capitalizedVariantName: String,
15
+ ) {
16
+ val mergeTaskName = "merge${capitalizedVariantName}ConsumerProguardFiles"
17
+ val mergeFileTask = project.tasks.named(mergeTaskName)
18
+
19
+ if (!mergeFileTask.isPresent) {
20
+ throw TaskNotFound("Task $mergeTaskName not found")
21
+ }
22
+
23
+ mergeFileTask.get().doLast {
24
+ val outputFile = it.outputs.files.singleFile
25
+ doLast(proguardRules, outputFile)
26
+ }
27
+ }
28
+
29
+ fun processGeneratedFiles(
30
+ proguardRules: List<File>,
31
+ capitalizedVariantName: String,
32
+ ) {
33
+ val mergeGenerateProguardTask: TaskProvider<*>?
34
+ val mergeName = "merge${capitalizedVariantName}GeneratedProguardFiles"
35
+ mergeGenerateProguardTask = project.tasks.named(mergeName)
36
+
37
+ mergeGenerateProguardTask.get().doLast {
38
+ val outputFile = it.outputs.files.singleFile
39
+ doLast(proguardRules, outputFile)
40
+ }
41
+ }
42
+
43
+ private fun doLast(
44
+ files: List<File>,
45
+ outputFile: File,
46
+ ) {
47
+ try {
48
+ val outputFileToMerge =
49
+ @Suppress("USELESS_IS_CHECK")
50
+ if (outputFile is File) {
51
+ outputFile
52
+ } else {
53
+ (outputFile as? RegularFileProperty)?.get()?.asFile
54
+ ?: error("Invalid output file")
55
+ }
56
+ Utils.mergeFiles(files, outputFileToMerge)
57
+ } catch (e: NoSuchFileException) {
58
+ Logging.log(e)
59
+ }
60
+ }
61
+ }
@@ -0,0 +1,26 @@
1
+ package com.callstack.react.brownfield.processors
2
+
3
+ import com.android.build.gradle.api.LibraryVariant
4
+ import com.callstack.react.brownfield.exceptions.TaskNotFound
5
+ import com.callstack.react.brownfield.utils.AndroidArchiveLibrary
6
+ import com.callstack.react.brownfield.utils.capitalized
7
+ import org.gradle.api.Project
8
+
9
+ object ResourceTaskProcessor {
10
+ fun process(
11
+ variant: LibraryVariant,
12
+ project: Project,
13
+ aarLibraries: List<AndroidArchiveLibrary>,
14
+ ) {
15
+ val taskPath = "generate${variant.name.capitalized()}Resources"
16
+ val resourceGenTask = project.tasks.named(taskPath)
17
+
18
+ if (!resourceGenTask.isPresent) {
19
+ throw TaskNotFound("Task $taskPath not found")
20
+ }
21
+
22
+ variant.registerGeneratedResFolders(
23
+ project.files(aarLibraries.map { it.getResDir() }),
24
+ )
25
+ }
26
+ }
@@ -0,0 +1,61 @@
1
+ package com.callstack.react.brownfield.processors
2
+
3
+ import com.callstack.react.brownfield.shared.BaseProject
4
+ import com.callstack.react.brownfield.utils.AndroidArchiveLibrary
5
+ import com.callstack.react.brownfield.utils.DirectoryManager
6
+ import com.callstack.react.brownfield.utils.capitalized
7
+ import org.gradle.api.file.ConfigurableFileCollection
8
+ import java.io.File
9
+ import java.nio.file.Files
10
+ import java.nio.file.Path
11
+ import java.nio.file.Paths
12
+
13
+ class VariantHelper : BaseProject() {
14
+ private fun getClassPathDirFiles(variantName: String): ConfigurableFileCollection {
15
+ return project.files(
16
+ "$buildDir/intermediates/javac/$variantName/compile${variantName.capitalized()}JavaWithJavac/classes",
17
+ )
18
+ }
19
+
20
+ fun classesMergeTaskDoFirst(
21
+ outputDir: File,
22
+ variantName: String,
23
+ ) {
24
+ val pathsToDelete = mutableListOf<Path>()
25
+ val javacDir = getClassPathDirFiles(variantName).first()
26
+ project.fileTree(outputDir).forEach { path ->
27
+ pathsToDelete.add(
28
+ Paths.get(outputDir.absolutePath).relativize(Paths.get(path.absolutePath)),
29
+ )
30
+ }
31
+ outputDir.deleteRecursively()
32
+ pathsToDelete.forEach { path ->
33
+ Files.deleteIfExists(Paths.get("$javacDir.absolutePath/$path"))
34
+ }
35
+ }
36
+
37
+ fun classesMergeTaskDoLast(
38
+ outputDir: File,
39
+ aarLibraries: Collection<AndroidArchiveLibrary>,
40
+ jarFiles: MutableList<File>,
41
+ variantName: String,
42
+ isMinifyEnabled: Boolean,
43
+ ) {
44
+ MergeProcessor.mergeClassesJarIntoClasses(project, aarLibraries, outputDir)
45
+ if (isMinifyEnabled) {
46
+ MergeProcessor.mergeLibsIntoClasses(project, aarLibraries, jarFiles, outputDir)
47
+ }
48
+ val javacDir = getClassPathDirFiles(variantName).first()
49
+ project.copy { copyTask ->
50
+ copyTask.from(outputDir)
51
+ copyTask.into(javacDir)
52
+ copyTask.exclude("META-INF/")
53
+ }
54
+
55
+ project.copy { copyTask ->
56
+ copyTask.from("${outputDir.absolutePath}/META-INF")
57
+ copyTask.into(DirectoryManager.getKotlinMetaDirectory(variantName))
58
+ copyTask.include("*.kotlin_module")
59
+ }
60
+ }
61
+ }