Skip to content

Commit 975231b

Browse files
romtsnclaude
andauthored
refactor(snapshot): Remove MergeClassesTask, scan classpath in-place (#1121)
Instead of merging all classes into an intermediate directory, ExportPreviewMetadataTask now scans directories and JARs directly from the classpath. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 9914349 commit 975231b

4 files changed

Lines changed: 58 additions & 130 deletions

File tree

plugin-build/src/main/kotlin/io/sentry/android/gradle/snapshot/metadata/ExportPreviewMetadataTask.kt

Lines changed: 56 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
package io.sentry.android.gradle.snapshot.metadata
22

3+
import com.android.build.gradle.BaseExtension
34
import groovy.json.JsonOutput
5+
import java.util.zip.ZipInputStream
46
import org.gradle.api.DefaultTask
57
import org.gradle.api.Project
6-
import org.gradle.api.file.DirectoryProperty
8+
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
9+
import org.gradle.api.attributes.Attribute
10+
import org.gradle.api.file.ConfigurableFileCollection
711
import org.gradle.api.file.RegularFileProperty
812
import org.gradle.api.provider.Property
913
import org.gradle.api.tasks.CacheableTask
1014
import org.gradle.api.tasks.Input
11-
import org.gradle.api.tasks.InputDirectory
15+
import org.gradle.api.tasks.InputFiles
1216
import org.gradle.api.tasks.OutputFile
1317
import org.gradle.api.tasks.PathSensitive
1418
import org.gradle.api.tasks.PathSensitivity
@@ -24,37 +28,29 @@ abstract class ExportPreviewMetadataTask : DefaultTask() {
2428

2529
@get:Input abstract val includePrivatePreviews: Property<Boolean>
2630

27-
@get:InputDirectory
31+
@get:InputFiles
2832
@get:PathSensitive(PathSensitivity.RELATIVE)
29-
abstract val mergedClassesDir: DirectoryProperty
33+
abstract val inputClasspath: ConfigurableFileCollection
3034

3135
@get:OutputFile abstract val outputFile: RegularFileProperty
3236

3337
@TaskAction
3438
fun export() {
3539
val scanner = PreviewMethodScanner(includePrivatePreviews.get())
36-
val rootDir = mergedClassesDir.get().asFile
3740

3841
// First pass: discover custom preview annotations.
3942
// Two iterations handle nested custom annotations where A references B but B was
4043
// processed after A in the first iteration.
4144
val customAnnotations = mutableMapOf<String, CustomPreviewAnnotation>()
4245
repeat(2) {
43-
rootDir
44-
.walk()
45-
.filter { it.isFile && it.name.endsWith(".class") }
46-
.forEach { file -> scanner.findCustomAnnotations(file.readBytes(), customAnnotations) }
46+
forEachClassEntry { _, bytes -> scanner.findCustomAnnotations(bytes, customAnnotations) }
4747
}
4848

4949
// Second pass: find preview methods
5050
val previews = mutableListOf<PreviewMetadata>()
51-
rootDir
52-
.walk()
53-
.filter { it.isFile && it.name.endsWith(".class") }
54-
.forEach { file ->
55-
val relativePath = file.relativeTo(rootDir).path
56-
scanClassFile(file.readBytes(), relativePath, scanner, customAnnotations, previews)
57-
}
51+
forEachClassEntry { relativePath, bytes ->
52+
scanClassFile(bytes, relativePath, scanner, customAnnotations, previews)
53+
}
5854

5955
val export = PreviewMetadataExport(previews = previews)
6056
val json = JsonOutput.prettyPrint(JsonOutput.toJson(export.toMap()))
@@ -66,6 +62,29 @@ abstract class ExportPreviewMetadataTask : DefaultTask() {
6662
logger.lifecycle("Exported ${previews.size} preview(s) to ${outFile.absolutePath}")
6763
}
6864

65+
private fun forEachClassEntry(action: (relativePath: String, bytes: ByteArray) -> Unit) {
66+
inputClasspath.files.forEach { file ->
67+
if (file.isDirectory) {
68+
file
69+
.walkTopDown()
70+
.filter { it.isFile && it.extension == "class" }
71+
.forEach { classFile ->
72+
val relativePath = classFile.relativeTo(file).path
73+
action(relativePath, classFile.readBytes())
74+
}
75+
} else if (file.isFile && (file.name.endsWith(".jar") || file.name.endsWith(".zip"))) {
76+
ZipInputStream(file.inputStream().buffered()).use { zis ->
77+
generateSequence { zis.nextEntry }
78+
.filter { !it.isDirectory && it.name.endsWith(".class") }
79+
.forEach { entry ->
80+
action(entry.name, zis.readBytes())
81+
zis.closeEntry()
82+
}
83+
}
84+
}
85+
}
86+
}
87+
6988
private fun scanClassFile(
7089
bytes: ByteArray,
7190
relativePath: String,
@@ -131,24 +150,43 @@ abstract class ExportPreviewMetadataTask : DefaultTask() {
131150
}
132151

133152
companion object {
153+
private const val ARTIFACT_TYPE = "artifactType"
134154

135155
fun register(
136156
project: Project,
137157
extension: SentrySnapshotMetadataExtension,
138-
mergeTask: TaskProvider<MergeClassesTask>,
158+
android: BaseExtension,
139159
): TaskProvider<ExportPreviewMetadataTask> {
140160
return project.tasks.register(
141161
"exportPreviewMetadata",
142162
ExportPreviewMetadataTask::class.java,
143163
) { task ->
144164
task.includePrivatePreviews.set(extension.includePrivatePreviews)
145-
task.mergedClassesDir.set(mergeTask.flatMap { it.outputDir })
165+
166+
// Local compiled classes
167+
// TODO Debug is hard coded here. we should allow different variants
168+
task.inputClasspath.from(project.tasks.named("compileDebugKotlin").map { it.outputs.files })
169+
170+
// Dependency project classes
171+
val debugClasspath = project.configurations.getByName("debugRuntimeClasspath")
172+
task.inputClasspath.from(
173+
debugClasspath.incoming
174+
.artifactView { view ->
175+
view.componentFilter { id -> id is ProjectComponentIdentifier }
176+
view.attributes { attrs ->
177+
attrs.attribute(Attribute.of(ARTIFACT_TYPE, String::class.java), "android-classes")
178+
}
179+
}
180+
.files
181+
)
146182

147183
task.outputFile.set(
148184
project.layout.buildDirectory.file(
149185
"sentry-snapshots/preview-metadata/preview-metadata.json"
150186
)
151187
)
188+
189+
task.dependsOn("compileDebugKotlin")
152190
}
153191
}
154192
}

plugin-build/src/main/kotlin/io/sentry/android/gradle/snapshot/metadata/MergeClassesTask.kt

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

plugin-build/src/main/kotlin/io/sentry/android/gradle/snapshot/metadata/SentrySnapshotMetadataPlugin.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ class SentrySnapshotMetadataPlugin : Plugin<Project> {
1616

1717
fun wireWithAndroid() {
1818
val android = project.extensions.getByType(BaseExtension::class.java)
19-
val mergeTask = MergeClassesTask.register(project, android)
20-
ExportPreviewMetadataTask.register(project, extension, mergeTask)
19+
ExportPreviewMetadataTask.register(project, extension, android)
2120
}
2221

2322
project.pluginManager.withPlugin("com.android.library") { wireWithAndroid() }

plugin-build/src/test/kotlin/io/sentry/android/gradle/snapshot/metadata/ExportPreviewMetadataTaskTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ class ExportPreviewMetadataTaskTest {
312312
return project.tasks
313313
.register("testExportPreviewMetadata", ExportPreviewMetadataTask::class.java) { task ->
314314
task.includePrivatePreviews.set(includePrivate)
315-
task.mergedClassesDir.set(classesDir)
315+
task.inputClasspath.from(classesDir)
316316
task.outputFile.set(outputFile)
317317
}
318318
.get()

0 commit comments

Comments
 (0)