diff --git a/README.md b/README.md index 7048e70..6792d00 100644 --- a/README.md +++ b/README.md @@ -112,9 +112,10 @@ and using the scoverage scalac plugin (`compileScoverageScala`). In cases where you only wish to generate reports / validate coverage, but are not interested in publishing the code, it is possible to only compile the code with the scoverage scalac plugin, thus reducing build times significantly. -In order to do so, simply add the arguments `-x compileScala` to the gradle execution. -For example: `gradle reportScoverage -x compileScala`. +In order to do so, simply add the arguments `-PscoverageCompileOnly` to the gradle execution. +For example: `gradle reportScoverage -PscoverageCompileOnly`. +Note that this mode is incompatible with parallel builds in multi-module projects. ### Compatibility with Consistent Versions Plugin @@ -122,6 +123,11 @@ In order for the plugin to work alongside [Palantir's consistent versions plugin the Scala version must be manually configured (via `scoverageScalaVersion`); otherwise, the plugin will attempt to resolve the compilation classpath, which is prohibited by the versions plugin. +Migration to 6.1.1 +---------------- + +* Running without normal compilation is now made with `-PscoverageCompileOnly` instead of `-x compileScala`. + Migration to 5.x ---------------- diff --git a/src/functionalTest/java/org/scoverage/ScalaMultiModuleTest.java b/src/functionalTest/java/org/scoverage/ScalaMultiModuleTest.java index 6442b1b..87f5c02 100644 --- a/src/functionalTest/java/org/scoverage/ScalaMultiModuleTest.java +++ b/src/functionalTest/java/org/scoverage/ScalaMultiModuleTest.java @@ -22,6 +22,17 @@ public void reportScoverage() { result.assertTaskExists("common:" + ScoveragePlugin.getREPORT_NAME()); } + @Test + public void reportScoverageParallel() { + + AssertableBuildResult result = dryRun("clean", ScoveragePlugin.getREPORT_NAME(), "--parallel"); + + result.assertTaskExists(ScoveragePlugin.getREPORT_NAME()); + result.assertTaskExists("a:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskExists("b:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskExists("common:" + ScoveragePlugin.getREPORT_NAME()); + } + @Test public void reportScoverageOnlyRoot() { @@ -52,7 +63,7 @@ public void reportScoverageOnlyA() { public void reportScoverageOnlyAWithoutNormalCompilation() { AssertableBuildResult result = run("clean", ":a:" + ScoveragePlugin.getREPORT_NAME(), - "-x", "compileScala"); + "-P" + ScoveragePlugin.getSCOVERAGE_COMPILE_ONLY_PROPERTY()); result.assertTaskSkipped("compileScala"); result.assertTaskSkipped("a:compileScala"); @@ -186,7 +197,7 @@ public void checkScoverageWithoutNormalCompilationAndWithoutCoverageInCommon() t AssertableBuildResult result = runAndFail("clean", ":a:test", ":common:test", "--tests", "org.hello.common.TestNothingCommonSuite", - "-x", "compileScala", + "-P" + ScoveragePlugin.getSCOVERAGE_COMPILE_ONLY_PROPERTY(), ScoveragePlugin.getCHECK_NAME()); result.assertTaskFailed("common:" + ScoveragePlugin.getCHECK_NAME()); @@ -246,7 +257,7 @@ public void checkAndAggregateScoverageWithoutCoverageInAll() throws Exception { public void aggregateScoverageWithoutNormalCompilation() throws Exception { AssertableBuildResult result = run("clean", ScoveragePlugin.getAGGREGATE_NAME(), - "-x", "compileScala"); + "-P" + ScoveragePlugin.getSCOVERAGE_COMPILE_ONLY_PROPERTY()); result.assertTaskSkipped("compileScala"); result.assertTaskSkipped("a:compileScala"); diff --git a/src/functionalTest/java/org/scoverage/ScalaMultiModuleWithMultipleTestTasksTest.java b/src/functionalTest/java/org/scoverage/ScalaMultiModuleWithMultipleTestTasksTest.java index 1905a1c..49d0bc1 100644 --- a/src/functionalTest/java/org/scoverage/ScalaMultiModuleWithMultipleTestTasksTest.java +++ b/src/functionalTest/java/org/scoverage/ScalaMultiModuleWithMultipleTestTasksTest.java @@ -193,7 +193,7 @@ public void checkScoverageWithoutNormalCompilationAndWithoutCoverageInCommon() t AssertableBuildResult result = runAndFail("clean", ":a:test", ":common:test", "--tests", "org.hello.common.TestNothingCommonSuite", - "-x", "compileScala", + "-P" + ScoveragePlugin.getSCOVERAGE_COMPILE_ONLY_PROPERTY(), ScoveragePlugin.getCHECK_NAME()); result.assertTaskFailed("common:" + ScoveragePlugin.getCHECK_NAME()); @@ -254,7 +254,7 @@ public void checkAndAggregateScoverageWithoutCoverageInAll() throws Exception { public void aggregateScoverageWithoutNormalCompilation() throws Exception { AssertableBuildResult result = run("clean", ScoveragePlugin.getAGGREGATE_NAME(), - "-x", "compileScala"); + "-P" + ScoveragePlugin.getSCOVERAGE_COMPILE_ONLY_PROPERTY()); result.assertTaskSkipped("compileScala"); result.assertTaskSkipped("a:compileScala"); diff --git a/src/functionalTest/java/org/scoverage/ScalaSingleModuleTest.java b/src/functionalTest/java/org/scoverage/ScalaSingleModuleTest.java index f93e184..de9da68 100644 --- a/src/functionalTest/java/org/scoverage/ScalaSingleModuleTest.java +++ b/src/functionalTest/java/org/scoverage/ScalaSingleModuleTest.java @@ -103,7 +103,7 @@ public void reportScoverageWithExcludedClasses() throws Exception { public void reportScoverageWithoutNormalCompilation() throws Exception { AssertableBuildResult result = run("clean", ScoveragePlugin.getREPORT_NAME(), - "-x", "compileScala"); + "-P" + ScoveragePlugin.getSCOVERAGE_COMPILE_ONLY_PROPERTY()); result.assertTaskSkipped("compileScala"); result.assertTaskSucceeded(ScoveragePlugin.getCOMPILE_NAME()); @@ -122,7 +122,7 @@ public void reportScoverageWithoutNormalCompilation() throws Exception { public void reportScoverageWithoutNormalCompilationAndWithExcludedClasses() throws Exception { AssertableBuildResult result = run("clean", ScoveragePlugin.getREPORT_NAME(), - "-PexcludedFile=.*", "-x", "compileScala"); + "-PexcludedFile=.*", "-P" + ScoveragePlugin.getSCOVERAGE_COMPILE_ONLY_PROPERTY()); Assert.assertTrue(resolve(reportDir(), "index.html").exists()); Assert.assertFalse(resolve(reportDir(), "src/main/scala/org/hello/World.scala.html").exists()); @@ -138,4 +138,4 @@ private void assertReportFilesExist() { Assert.assertTrue(resolve(reportDir(), "index.html").exists()); Assert.assertTrue(resolve(reportDir(), "src/main/scala/org/hello/World.scala.html").exists()); } -} \ No newline at end of file +} diff --git a/src/functionalTest/java/org/scoverage/ScalaSingleModuleWithMultipleTestTasksTest.java b/src/functionalTest/java/org/scoverage/ScalaSingleModuleWithMultipleTestTasksTest.java index 3d09d61..0511eb5 100644 --- a/src/functionalTest/java/org/scoverage/ScalaSingleModuleWithMultipleTestTasksTest.java +++ b/src/functionalTest/java/org/scoverage/ScalaSingleModuleWithMultipleTestTasksTest.java @@ -121,7 +121,7 @@ public void reportScoverageWithExcludedClasses() throws Exception { public void reportScoverageWithoutNormalCompilation() throws Exception { AssertableBuildResult result = run("clean", ScoveragePlugin.getREPORT_NAME(), - "-x", "compileScala"); + "-P" + ScoveragePlugin.getSCOVERAGE_COMPILE_ONLY_PROPERTY()); result.assertTaskSkipped("compileScala"); result.assertTaskSucceeded(ScoveragePlugin.getCOMPILE_NAME()); @@ -140,7 +140,7 @@ public void reportScoverageWithoutNormalCompilation() throws Exception { public void reportScoverageWithoutNormalCompilationAndWithExcludedClasses() throws Exception { AssertableBuildResult result = run("clean", ScoveragePlugin.getREPORT_NAME(), - "-PexcludedFile=.*", "-x", "compileScala"); + "-PexcludedFile=.*", "-P" + ScoveragePlugin.getSCOVERAGE_COMPILE_ONLY_PROPERTY()); Assert.assertTrue(resolve(reportDir(), "index.html").exists()); Assert.assertFalse(resolve(reportDir(), "src/main/scala/org/hello/World.scala.html").exists()); diff --git a/src/main/groovy/org/scoverage/ScoveragePlugin.groovy b/src/main/groovy/org/scoverage/ScoveragePlugin.groovy index a772060..19b0a13 100644 --- a/src/main/groovy/org/scoverage/ScoveragePlugin.groovy +++ b/src/main/groovy/org/scoverage/ScoveragePlugin.groovy @@ -25,6 +25,7 @@ class ScoveragePlugin implements Plugin { static final String COMPILE_NAME = 'compileScoverageScala' static final String AGGREGATE_NAME = 'aggregateScoverage' static final String DEFAULT_SCALA_VERSION = '2.13.6' + static final String SCOVERAGE_COMPILE_ONLY_PROPERTY = 'scoverageCompileOnly'; static final String DEFAULT_REPORT_DIR = 'reports' + File.separatorChar + 'scoverage' @@ -97,7 +98,6 @@ class ScoveragePlugin implements Plugin { def compileTask = project.tasks[instrumentedSourceSet.getCompileTaskName("scala")] compileTask.mustRunAfter(originalCompileTask) - originalJarTask.mustRunAfter(compileTask) def globalReportTask = project.tasks.register(REPORT_NAME, ScoverageAggregate) def globalCheckTask = project.tasks.register(CHECK_NAME) @@ -154,24 +154,6 @@ class ScoveragePlugin implements Plugin { configureCheckTask(project, extension, globalCheckTask, globalReportTask) - // make this project's scoverage compilation depend on scoverage compilation of any other project - // which this project depends on its normal compilation - // (essential when running without normal compilation on multi-module projects with inner dependencies) - def originalCompilationDependencies = recursiveDependenciesOf(compileTask).findAll { - it instanceof ScalaCompile - } - originalCompilationDependencies.each { - def dependencyProjectCompileTask = it.project.tasks.findByName(COMPILE_NAME) - def dependencyProjectReportTask = it.project.tasks.findByName(REPORT_NAME) - if (dependencyProjectCompileTask != null) { - compileTask.dependsOn(dependencyProjectCompileTask) - // we don't want this project's tests to affect the other project's report - testTasks.each { - it.mustRunAfter(dependencyProjectReportTask) - } - } - } - compileTask.configure { List parameters = [] List existingParameters = scalaCompileOptions.additionalParameters @@ -207,6 +189,79 @@ class ScoveragePlugin implements Plugin { } } + if (project.hasProperty(SCOVERAGE_COMPILE_ONLY_PROPERTY)) { + project.logger.info("Making scoverage compilation the primary compilation task (instead of compileScala)") + + originalCompileTask.enabled = false; + compileTask.destinationDirectory = originalCompileTask.destinationDirectory + originalJarTask.mustRunAfter(compileTask) + + // make this project's scoverage compilation depend on scoverage compilation of any other project + // which this project depends on its normal compilation + def originalCompilationDependencies = recursiveDependenciesOf(compileTask).findAll { + it instanceof ScalaCompile + } + originalCompilationDependencies.each { + def dependencyProjectCompileTask = it.project.tasks.findByName(COMPILE_NAME) + def dependencyProjectReportTask = it.project.tasks.findByName(REPORT_NAME) + if (dependencyProjectCompileTask != null) { + compileTask.dependsOn(dependencyProjectCompileTask) + // we don't want this project's tests to affect the other project's report + testTasks.each { + it.mustRunAfter(dependencyProjectReportTask) + } + } + } + } else { + compileTask.configure { + doFirst { + destinationDir.deleteDir() + } + + // delete non-instrumented classes by comparing normally compiled classes to those compiled with scoverage + doLast { + project.logger.info("Deleting classes compiled by scoverage but non-instrumented (identical to normal compilation)") + def originalCompileTaskName = project.sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME) + .getCompileTaskName("scala") + def originalDestinationDirectory = project.tasks[originalCompileTaskName].destinationDirectory + def originalDestinationDir = originalDestinationDirectory.get().asFile + def destinationDir = destinationDirectory.get().asFile + + + def findFiles = { File dir, Closure condition = null -> + def files = [] + + if (dir.exists()) { + dir.eachFileRecurse(FILES) { f -> + if (condition == null || condition(f)) { + def relativePath = dir.relativePath(f) + files << relativePath + } + } + } + + files + } + + def isSameFile = { String relativePath -> + def fileA = new File(originalDestinationDir, relativePath) + def fileB = new File(destinationDir, relativePath) + FileUtils.contentEquals(fileA, fileB) + } + + def originalClasses = findFiles(originalDestinationDir) + def identicalInstrumentedClasses = findFiles(destinationDir, { f -> + def relativePath = destinationDir.relativePath(f) + originalClasses.contains(relativePath) && isSameFile(relativePath) + }) + + identicalInstrumentedClasses.each { f -> + Files.deleteIfExists(destinationDir.toPath().resolve(f)) + } + } + } + } + project.gradle.taskGraph.whenReady { graph -> def hasAnyReportTask = reportTasks.any { graph.hasTask(it) } @@ -228,59 +283,6 @@ class ScoveragePlugin implements Plugin { } } } - - compileTask.configure { - if (!graph.hasTask(originalCompileTask)) { - project.logger.info("Making scoverage compilation the primary compilation task (instead of compileScala)") - destinationDirectory = originalCompileTask.destinationDirectory - } else { - doFirst { - def destinationDir = destinationDirectory.get().asFile - destinationDir.deleteDir() - } - - // delete non-instrumented classes by comparing normally compiled classes to those compiled with scoverage - doLast { - project.logger.info("Deleting classes compiled by scoverage but non-instrumented (identical to normal compilation)") - def originalCompileTaskName = project.sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME) - .getCompileTaskName("scala") - def originalDestinationDirectory = project.tasks[originalCompileTaskName].destinationDirectory - def originalDestinationDir = originalDestinationDirectory.get().asFile - def destinationDir = destinationDirectory.get().asFile - - def findFiles = { File dir, Closure condition = null -> - def files = [] - - if (dir.exists()) { - dir.eachFileRecurse(FILES) { f -> - if (condition == null || condition(f)) { - def relativePath = dir.relativePath(f) - files << relativePath - } - } - } - - files - } - - def isSameFile = { String relativePath -> - def fileA = new File(originalDestinationDir, relativePath) - def fileB = new File(destinationDir, relativePath) - FileUtils.contentEquals(fileA, fileB) - } - - def originalClasses = findFiles(originalDestinationDir) - def identicalInstrumentedClasses = findFiles(destinationDir, { f -> - def relativePath = destinationDir.relativePath(f) - originalClasses.contains(relativePath) && isSameFile(relativePath) - }) - - identicalInstrumentedClasses.each { f -> - Files.deleteIfExists(destinationDir.toPath().resolve(f)) - } - } - } - } } // define aggregation task