Skip to content

NewRelicConfigTask invalidates Gradle build cache on every build #495

Description

@trevjonez

Summary

The NewRelicConfigTask generates a NewRelicConfig.java source file containing a randomly generated BUILD_ID (via UUID.randomUUID()) and a build metrics string that change on every clean build. This generated source file is wired into the consuming app's compile task inputs via addGeneratedSourceDirectory() (AGP 7.4+) or registerJavaGeneratingTask() (AGP 4.x). Because the source content changes every build, the Java/Kotlin compile tasks see different inputs and cannot use Gradle's build cache — local or remote. Every CI build performs a full recompilation of the consuming app, even when no source code has actually changed.

Desired Behavior

Dynamic values generated by the New Relic Gradle plugin (build ID, metrics) should not invalidate the consuming app's compile or dex tasks. Expensive, cacheable tasks like compileReleaseJavaWithJavac, compileReleaseKotlin, and minifyReleaseWithR8 should remain cache-hits across CI builds when only the build ID changes.

Possible Solution

Replace the generated NewRelicConfig.java source file with a newrelic_config.json file placed in Android assets via addGeneratedSourceDirectory() on variant.sources.assets. At runtime, read the JSON from context.getAssets() during agent initialization and cache the values.

Why assets:

  • Assets are not compiled or dexed. They are merged during mergeAssets and packaged during APK assembly — both fast, I/O-bound tasks that are never meaningfully cached.
  • Compile tasks (javac, kotlinc) and dex tasks (R8/D8) never see the dynamic values, so they remain fully cacheable.
  • The build ID stays as a random UUID — no correctness risk, no change to the ID generation strategy.
  • DexGuard compatibility is preserved — DexGuard's asset encryption is transparent at runtime via its hooked AssetManager.

Alternatives considered and rejected:

  1. Make the build ID deterministic (e.g., derive from git SHA). Rejected because the build ID correlates crash reports with ProGuard/R8 mapping files. A deterministic ID would need to account for every possible input that affects the mapping file to guarantee correctness. This is extremely difficult and a failure (wrong mapping applied to a crash) is worse than a build performance issue.

  2. Generate bytecode directly via ASM and inject into the class pipeline after compilation. Rejected because the generated class JAR still feeds into R8/D8 dexing. This shifts the cache invalidation from compilation to dexing rather than eliminating it.

Additional context

For projects using Gradle remote build cache (Develocity/Gradle Enterprise), this issue eliminates one of the primary benefits of caching and significantly increases CI build times. The fields that cause cache misses on every build:

static final String BUILD_ID = "${buildId.get()}";       // UUID.randomUUID() — new value every clean build
static final String METRICS = "${buildMetrics.getOrElse("")}";  // contains build environment details

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions