fix(instrumentation): Resolve class availability at task time - #1402
fix(instrumentation): Resolve class availability at task time#1402sentry-junior[bot] wants to merge 14 commits into
Conversation
Move SDK runtime-optimization classpath resolution onto a dedicated task output so AGP no longer resolves *RuntimeClasspath while snapshotting instrumentation inputs during configuration. Refs #1399 Co-Authored-By: Roman Zavarnitsyn <roman.zavarnitsyn@sentry.io>
Co-Authored-By: Roman Zavarnitsyn <roman.zavarnitsyn@sentry.io>
Co-Authored-By: Roman Zavarnitsyn <roman.zavarnitsyn@sentry.io>
Assert the #1399 warning stays gone under --warning-mode all, and add a unit mutation oracle that register() leaves runtimeClasspath unresolved. Co-Authored-By: Roman Zavarnitsyn <roman.zavarnitsyn@sentry.io>
Derive module ids from resolutionResult.allComponents via a lazy Provider so presence matches the pre-#1402 graph path without config-time resolution, including modules without android-classes.
Co-Authored-By: Roman Zavarnitsyn <roman.zavarnitsyn@sentry.io>
runningcode
left a comment
There was a problem hiding this comment.
leaving you my comments from my partially completed review
|
|
||
| val availability = resolveClassAvailability(modules) | ||
| val output = outputFile.get().asFile | ||
| output.parentFile?.mkdirs() |
There was a problem hiding this comment.
we dont need this, gradle will create the directory marked as OutputFile
| output.parentFile?.mkdirs() |
There was a problem hiding this comment.
done — dropped the manual mkdirs().
| val output = outputFile.get().asFile | ||
| output.parentFile?.mkdirs() | ||
| // Write manually so the file stays deterministic for build caching (no Properties timestamp). | ||
| output.bufferedWriter().use { writer -> |
There was a problem hiding this comment.
can you add a comment here or somewhere as to what we expect the file to look like?
There was a problem hiding this comment.
added above the writer:
// Deterministic one-entry-per-line properties (no java.util.Properties timestamp header):
// androidx.core.view.ScrollingView=true
// timber.log.Timber=false
| val modulesService = | ||
| if (runtimeOptimizationsEnabled || tracingInstrumentationEnabled) { | ||
|
|
||
| // Runtime optimizations resolve the classpath via a task output so AGP does not force |
There was a problem hiding this comment.
I'm not sure I fully understood this comment. Isn't this an input not an output?
There was a problem hiding this comment.
yep — for the ASM visitor it's an @InputFile. the point of the task is only to produce that file at execution time so AGP doesn't force config-time resolution while snapshotting params. reworded the comment to call that out explicitly.
Drop manual OutputFile mkdirs, document the properties format, and explain that the task output is the instrumentation input.
Add a unit glue test from task file through visitor injection, and an integration test that checks the availability properties plus injected LoadClass bytecode after assemble with a supporting SDK. Co-Authored-By: Roman Zavarnitsyn <roman.zavarnitsyn@sentry.io>
Require the classAvailability injection marker when locating LoadClass under app/build so the E2E assertion cannot pick an uninstrumented library jar from walk order. Also search the TestKit transform cache where AsmClassesTransform writes dependency outputs.
CI showed the finder could return a stale instrumented jar from the shared TestKit transform cache (timber=false) even when this build's availability file had timber=true. Filter candidates by the expected map and prefer fresher ASM outputs.
Nested @InputFile visitor params are not reliable for dependency jars: AGP instruments them via isolated AsmClassesTransform, which does not promote nested file inputs into transform inputs/dependencies. CI showed the resolve task wrote timber=true while every instrumented LoadClass candidate had an empty or timber=false map. Restore a serializable @input MapProperty and populate it from the resolve task output so workers receive the availability values without config-time classpath resolution.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 4a568a8. Configure here.
TaskProvider.map { task -> task.outputFile.get() } can read the file
before the resolve task runs and snapshot an empty map into the ASM
worker. Wire classAvailability from
availabilityTask.flatMap { outputFile }.map(::readClassAvailability)
so Gradle tracks the produced file and only reads it after execution.
AsmClassesTransform isolates nested visitor @input MapProperty values when dependency jars are consumed. Isolation cannot query a task-mapped provider until the producing task has completed. Make merge/dex/transform consumers depend on resolveSentrySdkClassAvailability so the map is ready.
AsmClassesTransform isolation cannot query task-mapped nested @input MapProperty values (even after dependsOn ordered the resolve task). Restore RegularFileProperty @InputFile wiring and keep merge/dex/ transform consumers depending on the resolve task so the file exists when workers run.
|
Superseded by the generated app-class design. Class availability will be produced as normal generated app code, so runtime classpath resolution stays at task execution without manually ordering internal AGP transform consumers by task name. |

Fixes the config-time
*RuntimeClasspathresolution warning reported in #1399 after the runtime optimization work in #1375.Problem
AGP snapshots instrumentation
@Inputs during configuration. The availability map was derived fromconfiguration.incoming.resolutionResult, so enablingruntimeOptimizationsresolved the variant runtime classpath too early.Fix
ResolveSdkClassAvailabilityTask, following the existing deps-report pattern (artifactsFor(...).resolvedArtifacts).@InputFiletoSentrySdkOptimizationClassVisitorFactoryso dependency changes still invalidate instrumentation.afterResolve+SentryModulesServicepath; remove the modulesProviderreturn fromcollectModulesthat forced config-time resolution.Test plan
./gradlew -p plugin-build test --tests ResolveSdkClassAvailabilityTaskTest --tests SentryModulesCollectorTest --tests LoadClassClassVisitorTest./gradlew -p plugin-build spotlessApply compileKotlinruntimeOptimizationsenabledRefs #1399
Requested by roman.
--
View Junior Session [Sentry]