Skip to content

Commit 3ac2bc4

Browse files
authored
Optimize codenarc task on rerun (#11677)
refactor: Apply normalization on every project perf: Insulate the BuildTimeInstrumentationPlugin from changes using a ClasspathNormalizer BuildTimeInstrumentationPlugin install a post-processing step onto normal compile tasks. Flow: 1. It creates a resolvable `buildTimeInstrumentationPlugin` configuration 2. For each main `compileJava` / `compileGroovy` / `compileScala` task, it registers that configuration as a **compile-task input** 3. Then appends a `doLast("instrumentClasses", ...)` action to post-process compiled classes. That action runs Byte Buddy plugins against the compiled class output. If any input for that task changes, the whole compile task reruns. In particular the Byte Buddy plugins are tracked via ```kotlin inputs.files(project.configurations.named(BUILD_TIME_INSTRUMENTATION_PLUGIN_CONFIGURATION)) ``` In particular instrumentation plugins rely on instrumentation plugins coming from `agent-tooling` project which also happens to have a version file ```Gradle subProj.configurations.named('buildTimeInstrumentationPlugin') { it.dependencies.add(subProj.dependencies.project( path: ':dd-java-agent:agent-tooling', configuration: 'buildTimeInstrumentationToolingPlugins' )) ``` Before the normalizer, Gradle hashed the full jar contents of the build-time instrumentation plugin classpath. And as such the jars on that configuration changed only because their generated *.version changed after a new Git commit. And as such invalidated the task output. Adding the normalizer to this input only teaches Gradle to use the normalizaton declared in the project to ignore the version file on this input. Important nuance: `ClasspathNormalizer` is better than `CompileClasspathNormalizer` here. These are Byte Buddy plugins executed at build time, so method-body/resource changes in those plugin jars can change generated bytecode. We only want to ignore the known volatile version resource. --- Before this change the compile task amounted to a significant part when only the commit version changed ``` 327.901s compile* 10.486s codenarcTest 9.576s *Jar 8.217s process*Resources 2.208s expandAgentShadowJar* 2.032s generate*Index 1.241s writeVersionNumberFile ``` After this change ``` 14.780s compile* # up-to-date checking, not recompilation 7.469s process*Resources 6.194s *Jar 5.056s codenarcTest 2.233s writeVersionNumberFile 1.847s generate*Index 0.899s expandAgentShadowJar* ``` Co-authored-by: brice.dutheil <brice.dutheil@datadoghq.com>
1 parent 477a0ce commit 3ac2bc4

4 files changed

Lines changed: 10 additions & 68 deletions

File tree

build.gradle.kts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,13 @@ val compileTask = tasks.register("compile")
6666
allprojects {
6767
group = "com.datadoghq"
6868

69+
normalization {
70+
runtimeClasspath {
71+
// Let's ignore only version files generated by dd-trace-java.version-file
72+
ignore("**/*.version")
73+
}
74+
}
75+
6976
if (isCI.isPresent) {
7077
layout.buildDirectory = providers.provider {
7178
val newProjectCIPath = projectDir.path.replace(

buildSrc/src/main/kotlin/datadog/gradle/plugin/instrument/BuildTimeInstrumentationPlugin.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package datadog.gradle.plugin.instrument
33
import org.gradle.api.Plugin
44
import org.gradle.api.Project
55
import org.gradle.api.logging.Logging
6+
import org.gradle.api.tasks.ClasspathNormalizer
67
import org.gradle.api.tasks.SourceSet
78
import org.gradle.api.tasks.SourceSetContainer
89
import org.gradle.api.tasks.compile.AbstractCompile
@@ -115,6 +116,8 @@ class BuildTimeInstrumentationPlugin : Plugin<Project> {
115116
BUILD_TIME_INSTRUMENTATION_PLUGIN_CONFIGURATION
116117
)
117118
inputs.files(project.configurations.named(BUILD_TIME_INSTRUMENTATION_PLUGIN_CONFIGURATION))
119+
.withPropertyName(BUILD_TIME_INSTRUMENTATION_PLUGIN_CONFIGURATION)
120+
.withNormalizer(ClasspathNormalizer::class.java)
118121

119122
// Compute optional Java version.
120123
val match = Regex("compileMain_(.+)Java").matchEntire(compileTaskName)

buildSrc/src/main/kotlin/datadog/gradle/plugin/version/WriteVersionFilePlugin.kt

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,8 @@ class WriteVersionFilePlugin : Plugin<Project> {
1010
override fun apply(target: Project) {
1111
target.pluginManager.apply("java")
1212

13-
val versionFileName = "${target.name}.version"
1413
val writeVersionFile = target.tasks.register<WriteVersionFile>("writeVersionNumberFile")
1514

16-
// Keep volatile generated version metadata from invalidating @Classpath consumers such as CodeNarc.
17-
// https://docs.gradle.org/current/userguide/build_cache_concepts.html#runtime_classpath_normalization
18-
target.normalization {
19-
runtimeClasspath {
20-
ignore(versionFileName)
21-
}
22-
}
23-
2415
target.the<JavaPluginExtension>().sourceSets.named("main") {
2516
resources.srcDir(writeVersionFile)
2617
}

buildSrc/src/test/kotlin/datadog/gradle/plugin/version/WriteVersionFilePluginTest.kt

Lines changed: 0 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -125,65 +125,6 @@ class WriteVersionFilePluginTest : VersionPluginsFixture() {
125125
assertThat(versionFile).doesNotExist()
126126
}
127127

128-
@Test
129-
fun `generated version file is ignored in runtime classpath normalization`() {
130-
writeSettings(
131-
"""
132-
rootProject.name = "my-lib"
133-
"""
134-
)
135-
writeRootProject(
136-
"""
137-
import org.gradle.api.DefaultTask
138-
import org.gradle.api.file.ConfigurableFileCollection
139-
import org.gradle.api.file.RegularFileProperty
140-
import org.gradle.api.tasks.Classpath
141-
import org.gradle.api.tasks.InputFiles
142-
import org.gradle.api.tasks.OutputFile
143-
import org.gradle.api.tasks.TaskAction
144-
145-
plugins {
146-
id("dd-trace-java.version-file")
147-
}
148-
149-
version = "1.2.3"
150-
151-
tasks.named<datadog.gradle.plugin.version.WriteVersionFile>("writeVersionNumberFile") {
152-
gitHash.set(providers.gradleProperty("gitHash").orElse("abc12345"))
153-
}
154-
155-
abstract class ClasspathProbe : DefaultTask() {
156-
@get:InputFiles
157-
@get:Classpath
158-
val classpath: ConfigurableFileCollection = project.objects.fileCollection()
159-
160-
@get:OutputFile
161-
val outputFile: RegularFileProperty = project.objects.fileProperty()
162-
163-
@TaskAction
164-
fun probe() {
165-
outputFile.get().asFile.writeText("probed")
166-
}
167-
}
168-
169-
tasks.register<ClasspathProbe>("classpathProbe") {
170-
dependsOn("processResources")
171-
classpath.from(sourceSets.main.get().runtimeClasspath)
172-
outputFile.set(layout.buildDirectory.file("classpath-probe/output.txt"))
173-
}
174-
"""
175-
)
176-
177-
assertThat(run("classpathProbe", "-PgitHash=abc12345").task(":classpathProbe")?.outcome)
178-
.isEqualTo(TaskOutcome.SUCCESS)
179-
180-
val result = run("classpathProbe", "-PgitHash=def67890")
181-
182-
assertThat(generatedVersionFile).hasContent("1.2.3~def67890")
183-
assertThat(result.task(":writeVersionNumberFile")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
184-
assertThat(result.task(":classpathProbe")?.outcome).isEqualTo(TaskOutcome.UP_TO_DATE)
185-
}
186-
187128
private fun assertVersionFile(
188129
expectedContentRegex: String,
189130
task: String = ":writeVersionNumberFile",

0 commit comments

Comments
 (0)