Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change running without normal compilation to a property #165

Merged
merged 1 commit into from
Aug 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,16 +112,22 @@ 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

In order for the plugin to work alongside [Palantir's consistent versions plugin](https://github.com/palantir/gradle-consistent-versions),
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is what would normally be considered a breaking change - should we be releasing 7.0.0 here?

I'm sure there is a better way of managing updates to the README, but for now could we remove this file from the PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maiflai I was counting on you to make the right decision on whether this should bump the major version or not :)

----------------

* Running without normal compilation is now made with `-PscoverageCompileOnly` instead of `-x compileScala`.

Migration to 5.x
----------------

Expand Down
17 changes: 14 additions & 3 deletions src/functionalTest/java/org/scoverage/ScalaMultiModuleTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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() {

Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down Expand Up @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand All @@ -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());
Expand All @@ -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());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand All @@ -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());
Expand Down
146 changes: 74 additions & 72 deletions src/main/groovy/org/scoverage/ScoveragePlugin.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class ScoveragePlugin implements Plugin<PluginAware> {
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'

Expand Down Expand Up @@ -97,7 +98,6 @@ class ScoveragePlugin implements Plugin<PluginAware> {

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)
Expand Down Expand Up @@ -154,24 +154,6 @@ class ScoveragePlugin implements Plugin<PluginAware> {

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<String> parameters = []
List<String> existingParameters = scalaCompileOptions.additionalParameters
Expand Down Expand Up @@ -207,6 +189,79 @@ class ScoveragePlugin implements Plugin<PluginAware> {
}
}

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<Boolean> 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) }

Expand All @@ -228,59 +283,6 @@ class ScoveragePlugin implements Plugin<PluginAware> {
}
}
}

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<Boolean> 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
Expand Down