1
1
package org.scoverage
2
2
3
3
import org.apache.commons.io.FileUtils
4
+ import org.gradle.api.Action
4
5
import org.gradle.api.Plugin
5
6
import org.gradle.api.Project
6
7
import org.gradle.api.Task
7
8
import org.gradle.api.invocation.Gradle
8
9
import org.gradle.api.plugins.PluginAware
9
10
import org.gradle.api.plugins.scala.ScalaPlugin
11
+ import org.gradle.api.provider.Provider
10
12
import org.gradle.api.tasks.SourceSet
13
+ import org.gradle.api.tasks.Sync
11
14
import org.gradle.api.tasks.TaskProvider
12
15
import org.gradle.api.tasks.scala.ScalaCompile
13
16
import org.gradle.api.tasks.testing.Test
17
+ import org.gradle.api.tasks.util.PatternFilterable
14
18
15
19
import java.nio.file.Files
16
20
import java.util.concurrent.ConcurrentHashMap
@@ -20,6 +24,7 @@ import static groovy.io.FileType.FILES
20
24
class ScoveragePlugin implements Plugin<PluginAware > {
21
25
22
26
static final String CONFIGURATION_NAME = ' scoverage'
27
+ static final String MERGE_MEASUREMENTS_NAME = ' mergeScoverageMeasurements'
23
28
static final String REPORT_NAME = ' reportScoverage'
24
29
static final String CHECK_NAME = ' checkScoverage'
25
30
static final String COMPILE_NAME = ' compileScoverageScala'
@@ -85,6 +90,26 @@ class ScoveragePlugin implements Plugin<PluginAware> {
85
90
}
86
91
87
92
private void createTasks (Project project , ScoverageExtension extension ) {
93
+ /**
94
+ dataDir is split into subdirectories:
95
+ workDir
96
+ {testTaskName}MeasurementsDir
97
+ {testTaskName}ReportDir.
98
+
99
+ workDir
100
+ Directory where metadata/measurements are "produced"
101
+ Two tasks produce files in that directory: compile and test. Because of that, this directory is not
102
+ cacheable.
103
+ Only one file from this directory is cached: metadata from compile task (because it is a single file).
104
+
105
+ testMeasurementsDir
106
+ Directory where measurements are synced from "workDir". It is registered as an additional output to test task,
107
+ which makes the measurements files cacheable.
108
+
109
+ reportDir
110
+ Merges workDir/scoverage.coverage and testMeasurementsDir/scoverage.measurements.* for reporting.
111
+ */
112
+ def dataWorkDir = extension. dataDir. map {new File (it, " work" ) }
88
113
89
114
ScoverageRunner scoverageRunner = new ScoverageRunner (project. configurations. scoverage)
90
115
@@ -106,6 +131,9 @@ class ScoveragePlugin implements Plugin<PluginAware> {
106
131
def compileTask = project. tasks[instrumentedSourceSet. getCompileTaskName(" scala" )]
107
132
compileTask. mustRunAfter(originalCompileTask)
108
133
134
+ // merges measurements from individual reports into one directory for use by globalReportTask
135
+ def globalMergeMeasurementsTask = project. tasks. register(MERGE_MEASUREMENTS_NAME , Sync . class)
136
+
109
137
def globalReportTask = project. tasks. register(REPORT_NAME , ScoverageAggregate )
110
138
def globalCheckTask = project. tasks. register(CHECK_NAME )
111
139
@@ -122,17 +150,31 @@ class ScoveragePlugin implements Plugin<PluginAware> {
122
150
List<ScoverageReport > reportTasks = testTasks. collect { testTask ->
123
151
testTask. mustRunAfter(compileTask)
124
152
125
- def reportTaskName = " report${ testTask.name.capitalize()} Scoverage"
126
- def taskReportDir = project. file(" ${ project.buildDir} /reports/scoverage${ testTask.name.capitalize()} " )
153
+ def cTaskName = testTask. name. capitalize()
154
+
155
+ def reportTaskName = " report${ cTaskName} Scoverage"
156
+ def taskReportDir = project. file(" ${ project.buildDir} /reports/scoverage${ cTaskName} " )
157
+
158
+ def scoverageSyncMetaWithOutputs =
159
+ project. tasks. register(" sync${ cTaskName} ScoverageData" , Sync . class)
160
+
161
+ scoverageSyncMetaWithOutputs. configure {
162
+ dependsOn compileTask, testTask
163
+ from(dataWorkDir) {
164
+ include(" scoverage.coverage" )
165
+ }
166
+ from(dataMeasurementsDir(extension, cTaskName))
167
+ into(dataReportDir(extension, cTaskName))
168
+ }
127
169
128
170
project. tasks. create(reportTaskName, ScoverageReport ) {
129
- dependsOn originalJarTask, compileTask, testTask
130
- onlyIf { extension . dataDir . get(). list() }
171
+ dependsOn originalJarTask, compileTask, testTask, scoverageSyncMetaWithOutputs
172
+ onlyIf { scoverageSyncMetaWithOutputs . get() . getDestinationDir (). list() }
131
173
group = ' verification'
132
174
runner = scoverageRunner
133
175
reportDir = taskReportDir
134
176
sources = originalSourceSet. scala. getSourceDirectories()
135
- dataDir = extension . dataDir
177
+ dataDir = scoverageSyncMetaWithOutputs . map {it . getDestinationDir()}
136
178
sourceEncoding. set(detectedSourceEncoding)
137
179
coverageOutputCobertura = extension. coverageOutputCobertura
138
180
coverageOutputXML = extension. coverageOutputXML
@@ -141,17 +183,29 @@ class ScoveragePlugin implements Plugin<PluginAware> {
141
183
}
142
184
}
143
185
144
- globalReportTask. configure {
186
+ globalMergeMeasurementsTask. configure {sync ->
187
+ dependsOn(reportTasks)
188
+ dependsOn(compileTask)
189
+
145
190
def dataDirs = reportTasks. findResults { it. dataDir. get() }
146
191
147
- dependsOn reportTasks
148
- onlyIf { dataDirs. any { it. list() } }
192
+ from(dataWorkDir. map {new File (it, ' scoverage.coverage' ) })
193
+ from(dataDirs) {
194
+ exclude(" scoverage.coverage" )
195
+ }
196
+ into(project. file(" ${ project.buildDir} /mergedScoverage" ))
197
+ }
198
+
199
+ globalReportTask. configure {
200
+ dependsOn globalMergeMeasurementsTask
201
+
202
+ onlyIf { globalMergeMeasurementsTask. get(). getDestinationDir(). list()}
149
203
150
204
group = ' verification'
151
205
runner = scoverageRunner
152
206
reportDir = extension. reportDir
153
207
sources = originalSourceSet. scala. getSourceDirectories()
154
- dirsToAggregateFrom = dataDirs
208
+ dirsToAggregateFrom = globalMergeMeasurementsTask . map {[it . getDestinationDir()]}
155
209
sourceEncoding. set(detectedSourceEncoding)
156
210
deleteReportsOnAggregation = false
157
211
coverageOutputCobertura = extension. coverageOutputCobertura
@@ -170,8 +224,12 @@ class ScoveragePlugin implements Plugin<PluginAware> {
170
224
}
171
225
172
226
def scalaVersion = resolveScalaVersions(project)
227
+
228
+ // the compile task creates a store of measured statements
229
+ outputs. file(dataWorkDir. map {new File (it, ' scoverage.coverage' ) })
230
+
173
231
if (scalaVersion. majorVersion < 3 ) {
174
- parameters. add(" -P:scoverage:dataDir:${ extension.dataDir .get().absolutePath} " . toString())
232
+ parameters. add(" -P:scoverage:dataDir:${ dataWorkDir .get().absolutePath} " . toString())
175
233
parameters. add(" -P:scoverage:sourceRoot:${ extension.project.getRootDir().absolutePath} " . toString())
176
234
if (extension. excludedPackages. get()) {
177
235
def packages = extension. excludedPackages. get(). join(' ;' )
@@ -185,10 +243,7 @@ class ScoveragePlugin implements Plugin<PluginAware> {
185
243
parameters. add(' -Yrangepos' )
186
244
}
187
245
scalaCompileOptions. additionalParameters = parameters
188
- // the compile task creates a store of measured statements
189
- outputs. file(new File (extension. dataDir. get(), ' scoverage.coverage' ))
190
-
191
- dependsOn project. configurations[CONFIGURATION_NAME ]
246
+ dependsOn project. configurations. named(CONFIGURATION_NAME )
192
247
doFirst {
193
248
/*
194
249
It is crucial that this would run in `doFirst`, as this resolves the (dependencies of the)
@@ -205,7 +260,7 @@ class ScoveragePlugin implements Plugin<PluginAware> {
205
260
}
206
261
} else {
207
262
parameters. add(" -sourceroot:${ project.rootDir.absolutePath} " . toString())
208
- parameters. add(" -coverage-out:${ extension.dataDir .get().absolutePath} " . toString())
263
+ parameters. add(" -coverage-out:${ dataWorkDir .get().absolutePath} " . toString())
209
264
scalaCompileOptions. additionalParameters = parameters
210
265
}
211
266
}
@@ -262,20 +317,26 @@ class ScoveragePlugin implements Plugin<PluginAware> {
262
317
def hasAnyReportTask = reportTasks. any { graph. hasTask(it) }
263
318
264
319
if (hasAnyReportTask) {
320
+
265
321
project. tasks. withType(Test ). each { testTask ->
266
322
testTask. configure {
323
+ def cTaskName = testTask. name. capitalize()
324
+
267
325
project. logger. info(" Adding instrumented classes to '${ path} ' classpath" )
268
326
269
327
classpath = project. configurations. scoverage + instrumentedSourceSet. output + classpath
270
328
271
- outputs . upToDateWhen {
272
- extension . dataDir . get() . listFiles( new FilenameFilter () {
273
- @Override
274
- boolean accept ( File dir , String name ) {
275
- name . startsWith( " scoverage.measurements. " )
276
- }
277
- } )
329
+ doLast {
330
+ project . sync {
331
+ from(dataWorkDir)
332
+ exclude( " scoverage.coverage " )
333
+ into(dataMeasurementsDir(extension, cTaskName) )
334
+ }
335
+ project . delete(project . fileTree(dataWorkDir) . exclude( " scoverage.coverage " ) )
278
336
}
337
+
338
+ outputs. dir(dataMeasurementsDir(extension, cTaskName)). withPropertyName(" scoverage.measurements" )
339
+
279
340
}
280
341
}
281
342
}
@@ -323,6 +384,14 @@ class ScoveragePlugin implements Plugin<PluginAware> {
323
384
}
324
385
}
325
386
387
+ private Provider<File > dataMeasurementsDir (ScoverageExtension extension , String testName ) {
388
+ return extension. dataDir. map {new File (it, " ${ testName} Measurements" ) }
389
+ }
390
+
391
+ private Provider<File > dataReportDir (ScoverageExtension extension , String testName ) {
392
+ return extension. dataDir. map { new File (it, " ${ testName} Report" ) }
393
+ }
394
+
326
395
private void configureCheckTask (Project project , ScoverageExtension extension ,
327
396
TaskProvider<Task > globalCheckTask ,
328
397
TaskProvider<ScoverageAggregate > globalReportTask ) {
0 commit comments