Skip to content

Commit 3293d86

Browse files
Merge pull request #6965 from DataDog/bbujon/fix-ci-build-cache
Improve custom Instrumenter Gradle plugin
2 parents 0aadf20 + 40bfa2f commit 3293d86

1 file changed

Lines changed: 83 additions & 44 deletions

File tree

buildSrc/src/main/groovy/InstrumentPlugin.groovy

Lines changed: 83 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -7,34 +7,42 @@ import org.gradle.api.file.DirectoryProperty
77
import org.gradle.api.invocation.BuildInvocationDetails
88
import org.gradle.api.provider.ListProperty
99
import org.gradle.api.provider.Property
10+
import org.gradle.api.tasks.Classpath
11+
import org.gradle.api.tasks.Input
12+
import org.gradle.api.tasks.InputDirectory
13+
import org.gradle.api.tasks.InputFiles
14+
import org.gradle.api.tasks.Optional
15+
import org.gradle.api.tasks.OutputDirectory
16+
import org.gradle.api.tasks.TaskAction
1017
import org.gradle.api.tasks.compile.AbstractCompile
1118
import org.gradle.jvm.toolchain.JavaLanguageVersion
1219
import org.gradle.jvm.toolchain.JavaToolchainService
1320
import org.gradle.workers.WorkAction
1421
import org.gradle.workers.WorkParameters
1522
import org.gradle.workers.WorkerExecutor
1623

24+
import javax.inject.Inject
1725
import java.util.regex.Matcher
1826

1927
/**
2028
* instrument<Language> task plugin which performs build-time instrumentation of classes.
2129
*/
30+
@SuppressWarnings('unused')
2231
class InstrumentPlugin implements Plugin<Project> {
23-
2432
@Override
2533
void apply(Project project) {
2634
InstrumentExtension extension = project.extensions.create('instrument', InstrumentExtension)
2735

2836
project.tasks.matching {
2937
it.name in ['compileJava', 'compileScala', 'compileKotlin'] ||
30-
it.name =~ /compileMain_(?:.+)Java/
38+
it.name =~ /compileMain_.+Java/
3139
}.all {
3240
AbstractCompile compileTask = it as AbstractCompile
3341
Matcher versionMatcher = it.name =~ /compileMain_(.+)Java/
3442
project.afterEvaluate {
3543
if (!compileTask.source.empty) {
36-
String sourceSetSuffix
37-
String javaVersion
44+
String sourceSetSuffix = null
45+
String javaVersion = null
3846
if (versionMatcher.matches()) {
3947
sourceSetSuffix = versionMatcher.group(1)
4048
if (sourceSetSuffix ==~ /java\d+/) {
@@ -50,14 +58,28 @@ class InstrumentPlugin implements Plugin<Project> {
5058

5159
// insert task between compile and jar, and before test*
5260
String instrumentTaskName = compileTask.name.replace('compile', 'instrument')
53-
def instrumentTask = project.task(['type': InstrumentTask], instrumentTaskName) {
54-
description = "Instruments the classes compiled by ${compileTask.name}"
55-
doLast {
56-
instrument(javaVersion, project, extension, rawClassesDir, classesDir, it)
61+
def instrumentTask = project.tasks.register(instrumentTaskName, InstrumentTask) {
62+
// Task configuration
63+
it.group = 'Byte Buddy'
64+
it.description = "Instruments the classes compiled by ${compileTask.name}"
65+
it.inputs.dir(compileTask.destinationDirectory)
66+
it.outputs.dir(classesDir)
67+
// Task inputs
68+
it.javaVersion = javaVersion
69+
def instrumenterConfiguration = project.configurations.named('instrumentPluginClasspath')
70+
if (instrumenterConfiguration.present) {
71+
it.pluginClassPath.from(instrumenterConfiguration.get())
5772
}
73+
it.plugins = extension.plugins
74+
it.instrumentingClassPath.from(
75+
findCompileClassPath(project, it.name) +
76+
rawClassesDir +
77+
findAdditionalClassPath(extension, it.name)
78+
)
79+
it.sourceDirectory = rawClassesDir
80+
// Task output
81+
it.targetDirectory = classesDir
5882
}
59-
instrumentTask.inputs.dir(compileTask.destinationDirectory)
60-
instrumentTask.outputs.dir(classesDir)
6183
if (javaVersion) {
6284
project.tasks.named(project.sourceSets."main_java${javaVersion}".classesTaskName) {
6385
it.dependsOn(instrumentTask)
@@ -71,6 +93,22 @@ class InstrumentPlugin implements Plugin<Project> {
7193
}
7294
}
7395
}
96+
97+
static findCompileClassPath(Project project, String taskName) {
98+
def matcher = taskName =~ /instrument([A-Z].+)Java/
99+
def cfgName = matcher.matches() ? "${matcher.group(1).uncapitalize()}CompileClasspath" : 'compileClasspath'
100+
project.configurations.named(cfgName).findAll {
101+
it.name != 'previous-compilation-data.bin' && !it.name.endsWith('.gz')
102+
}
103+
}
104+
105+
static findAdditionalClassPath(InstrumentExtension extension, String taskName) {
106+
extension.additionalClasspath.getOrDefault(taskName, []).collect {
107+
// insert intermediate 'raw' directory for unprocessed classes
108+
def fileName = it.get().asFile.name
109+
it.get().dir("../${fileName.replaceFirst('^main', 'raw')}")
110+
}
111+
}
74112
}
75113

76114
abstract class InstrumentExtension {
@@ -79,51 +117,52 @@ abstract class InstrumentExtension {
79117
}
80118

81119
abstract class InstrumentTask extends DefaultTask {
82-
{
83-
group = 'Byte Buddy'
84-
}
85-
86-
@javax.inject.Inject
120+
@Input @Optional
121+
String javaVersion
122+
@InputFiles @Classpath
123+
abstract ConfigurableFileCollection getPluginClassPath()
124+
@Input
125+
ListProperty<String> plugins
126+
@InputFiles @Classpath
127+
abstract ConfigurableFileCollection getInstrumentingClassPath()
128+
@InputDirectory
129+
Directory sourceDirectory
130+
131+
@OutputDirectory
132+
Directory targetDirectory
133+
134+
@Inject
87135
abstract JavaToolchainService getJavaToolchainService()
88-
89-
@javax.inject.Inject
136+
@Inject
90137
abstract BuildInvocationDetails getInvocationDetails()
91-
92-
@javax.inject.Inject
138+
@Inject
93139
abstract WorkerExecutor getWorkerExecutor()
94140

95-
void instrument(String javaVersion,
96-
Project project,
97-
InstrumentExtension extension,
98-
Directory sourceDirectory,
99-
Directory targetDirectory,
100-
InstrumentTask instrumentTask)
101-
{
102-
def workQueue
103-
if (javaVersion) {
104-
def javaLauncher = javaToolchainService.launcherFor { spec ->
105-
spec.languageVersion.set(JavaLanguageVersion.of(javaVersion))
141+
@TaskAction
142+
instrument() {
143+
workQueue().submit(InstrumentAction.class, parameters -> {
144+
parameters.buildStartedTime.set(this.invocationDetails.buildStartedTime)
145+
parameters.pluginClassPath.from(this.pluginClassPath)
146+
parameters.plugins.set(this.plugins)
147+
parameters.instrumentingClassPath.setFrom(this.instrumentingClassPath)
148+
parameters.sourceDirectory.set(this.sourceDirectory.asFile)
149+
parameters.targetDirectory.set(this.targetDirectory.asFile)
150+
})
151+
}
152+
153+
private workQueue() {
154+
if (this.javaVersion) {
155+
def javaLauncher = this.javaToolchainService.launcherFor { spec ->
156+
spec.languageVersion.set(JavaLanguageVersion.of(this.javaVersion))
106157
}.get()
107-
workQueue = workerExecutor.processIsolation { spec ->
158+
return this.workerExecutor.processIsolation { spec ->
108159
spec.forkOptions { fork ->
109160
fork.executable = javaLauncher.executablePath
110161
}
111162
}
112163
} else {
113-
workQueue = workerExecutor.noIsolation()
164+
return this.workerExecutor.noIsolation()
114165
}
115-
workQueue.submit(InstrumentAction.class, parameters -> {
116-
parameters.buildStartedTime.set(invocationDetails.buildStartedTime)
117-
parameters.pluginClassPath.setFrom(project.configurations.findByName('instrumentPluginClasspath') ?: [])
118-
parameters.plugins.set(extension.plugins)
119-
def matcher = instrumentTask.name =~ /instrument([A-Z].+)Java/
120-
def cfgName = matcher.matches() ? "${matcher.group(1).uncapitalize()}CompileClasspath" : 'compileClasspath'
121-
parameters.instrumentingClassPath.setFrom(project.configurations[cfgName].findAll {
122-
it.name != 'previous-compilation-data.bin' && !it.name.endsWith(".gz")
123-
} + sourceDirectory + (extension.additionalClasspath[instrumentTask.name] ?: [])*.get())
124-
parameters.sourceDirectory.set(sourceDirectory.asFile)
125-
parameters.targetDirectory.set(targetDirectory.asFile)
126-
})
127166
}
128167
}
129168

0 commit comments

Comments
 (0)