Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/aar-local-jars.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@callstack/react-native-brownfield': patch
---

fix: package classes from libraries' local jars (`libs/*.jar`) into the Android AAR; previously host apps crashed with NoClassDefFoundError. If your app also has those classes, because it added the jar itself as a workaround or depends on a library that the jar bundles, the build now fails with duplicate classes: remove the app's copy
2 changes: 1 addition & 1 deletion apps/RNApp/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ buildscript {
classpath("com.android.tools.build:gradle")
classpath("com.facebook.react:react-native-gradle-plugin")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin")
classpath("com.callstack.react:brownfield-gradle-plugin:2.0.0-alpha11-SNAPSHOT")
classpath("com.callstack.react:brownfield-gradle-plugin:2.0.0-alpha12-SNAPSHOT")
}
}

Expand Down
2 changes: 1 addition & 1 deletion apps/scripts/prepare-android-build-gradle-for-ci.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ if (!projectDirName) {

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const SNAPSHOT_VERSION = '2.0.0-alpha11-SNAPSHOT';
const SNAPSHOT_VERSION = '2.0.0-alpha12-SNAPSHOT';
// Accepts a repo-relative app dir (e.g. "ExpoApp58") or an absolute path (e.g.
// the scaffold-smoke app created outside the repo).
const projectDir = path.isAbsolute(projectDirName)
Expand Down
2 changes: 1 addition & 1 deletion gradle-plugins/react/brownfield/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
PROJECT_ID=com.callstack.react.brownfield
ARTIFACT_ID=brownfield-gradle-plugin
VERSION=2.0.0-alpha11
VERSION=2.0.0-alpha12
GROUP=com.callstack.react
IMPLEMENTATION_CLASS=com.callstack.react.brownfield.plugin.RNBrownfieldPlugin

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ class VariantTaskProvider(val project: Project) {
it.dependsOn(VariantHelper.getJavaCompileTask(project, capitalizedVariantName))
it.variantName.set(variantName)
it.inputClassesJars.from(aarLibraries.map { aarLibrary -> aarLibrary.getClassesJarFile() })
// libs/*.jar of each library (#489)
it.inputClassesJars.from(
aarLibraries.map { aarLibrary -> project.fileTree(aarLibrary.getLibsDir()) { tree -> tree.include("*.jar") } },
)
it.outputDir.set(DirectoryManager.getMergeClassDirectory(variantName))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ abstract class MergeClassesTask : DefaultTask() {
copyTask.from(mergedOutputDir)
copyTask.into(javacDir)
copyTask.exclude("META-INF/")
// Root module descriptor of a library's local jar, not a class of the AAR.
copyTask.exclude("module-info.class")
}

project.copy { copyTask ->
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.callstack.react.brownfield.processors

import com.callstack.react.brownfield.shared.ExplodeAarTask
import com.callstack.react.brownfield.shared.UnresolvedArtifactInfo
import com.callstack.react.brownfield.utils.AndroidArchiveLibrary
import com.callstack.react.brownfield.utils.DirectoryManager
import org.gradle.testfixtures.ProjectBuilder
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.io.TempDir
import java.io.File
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
import kotlin.test.assertTrue

/**
* A library that ships `implementation files("libs/some.jar")` has that jar in its exploded AAR's
* `libs/`. Its classes must be merged with the library's own `classes.jar` (#489).
*/
class MergeClassesLocalJarsTest {
@TempDir
lateinit var tempDir: File

@Test
fun `merges classes from a library's local jars`() {
val project = ProjectBuilder.builder().withProjectDir(tempDir).build()
DirectoryManager.project = project

val variantName = "release"
val library =
AndroidArchiveLibrary(
project,
UnresolvedArtifactInfo("RNApp", "react-native-aar-repro", "unspecified", null, false),
variantName,
)
val explodeTask = project.tasks.register("explodeTest", ExplodeAarTask::class.java)
val mergeTask = VariantTaskProvider(project).mergeClasses(listOf(library), explodeTask, variantName).get()

// Exploded after the task is configured, as on a clean build.
library.getExplodedAarRootDir().mkdirs()
writeJar(library.getClassesJarFile(), "com/aarrepro/AarReproPackage.class")
writeJar(
File(library.getLibsDir(), "aar-repro.jar"),
"com/aarrepro/jar/AarReproJarClass.class",
"module-info.class",
)

mergeTask.run()

val merged = mergeTask.outputDir.get().asFile
assertTrue(File(merged, "com/aarrepro/AarReproPackage.class").exists(), "classes.jar was not merged")
assertTrue(
File(merged, "com/aarrepro/jar/AarReproJarClass.class").exists(),
"classes from libs/aar-repro.jar were not merged",
)
val javacDir = File(tempDir, "build/intermediates/javac/release/compileReleaseJavaWithJavac/classes")
assertTrue(File(javacDir, "com/aarrepro/jar/AarReproJarClass.class").exists(), "jar classes are not compiled into the AAR")
assertTrue(!File(javacDir, "module-info.class").exists(), "a jar's module-info.class ended up in the AAR")
}

private fun writeJar(
jar: File,
vararg entries: String,
) {
jar.parentFile.mkdirs()
ZipOutputStream(jar.outputStream()).use { zip ->
entries.forEach { entry ->
zip.putNextEntry(ZipEntry(entry))
zip.write(byteArrayOf(0xCA.toByte(), 0xFE.toByte(), 0xBA.toByte(), 0xBE.toByte()))
zip.closeEntry()
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ describe('gradleHelpers', () => {
}`;

expect(modifyRootBuildGradle(contents)).toContain(
'classpath("com.callstack.react:brownfield-gradle-plugin:2.0.0-alpha11")'
'classpath("com.callstack.react:brownfield-gradle-plugin:2.0.0-alpha12")'
);
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export const BROWNFIELD_PLUGIN_VERSION = '2.0.0-alpha11';
export const BROWNFIELD_PLUGIN_VERSION = '2.0.0-alpha12';
export const brownfieldGradlePluginDependency = `classpath("com.callstack.react:brownfield-gradle-plugin:${BROWNFIELD_PLUGIN_VERSION}")`;
Loading