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:
-
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.
-
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
Summary
The
NewRelicConfigTaskgenerates aNewRelicConfig.javasource file containing a randomly generatedBUILD_ID(viaUUID.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 viaaddGeneratedSourceDirectory()(AGP 7.4+) orregisterJavaGeneratingTask()(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, andminifyReleaseWithR8should remain cache-hits across CI builds when only the build ID changes.Possible Solution
Replace the generated
NewRelicConfig.javasource file with anewrelic_config.jsonfile placed in Android assets viaaddGeneratedSourceDirectory()onvariant.sources.assets. At runtime, read the JSON fromcontext.getAssets()during agent initialization and cache the values.Why assets:
mergeAssetsand packaged during APK assembly — both fast, I/O-bound tasks that are never meaningfully cached.javac,kotlinc) and dex tasks (R8/D8) never see the dynamic values, so they remain fully cacheable.AssetManager.Alternatives considered and rejected:
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.
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: