Skip to content
This repository has been archived by the owner on Feb 11, 2022. It is now read-only.

Introduce lazy tasks except for ktlint #184

Merged
merged 21 commits into from May 13, 2019
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
0ef4a03
Merge pull request #182 from novoda/develop
tasomaniac Mar 10, 2019
d755090
Lazy tasks for Detekt
tasomaniac Apr 13, 2019
006d143
Upgrade sample Kotlin version for running tests on Gradle 5
tasomaniac Apr 13, 2019
451ca3b
Lazy tasks for Lint
tasomaniac Apr 13, 2019
77d79d2
Lazy tasks for Pmd
tasomaniac Apr 13, 2019
a52f78c
Lazy tasks for java projects for all Java CodeQuality tools
tasomaniac May 2, 2019
531aedc
Lazy tasks for Android projects for all Java CodeQuality tools
tasomaniac May 2, 2019
758cf8f
Remove last bit of non-lazy task creation
tasomaniac May 2, 2019
eed3a7c
shuffle methods for clarity
tasomaniac May 2, 2019
0597e2d
Configure Findbugs per variant again instead of sourceSets
tasomaniac May 4, 2019
9384b6a
clean-up
tasomaniac May 4, 2019
542432a
Merge branch 'master' into taso/lazy-tasks-except-for-ktlint
tasomaniac May 4, 2019
eb86bc1
Make the meta task name unique per tool
tasomaniac May 4, 2019
71058cf
Merge branch 'develop' of github.com:novoda/gradle-static-analysis-pl…
tasomaniac May 8, 2019
1771f1e
Merge branch 'develop' into taso/lazy-tasks-except-for-ktlint
tasomaniac May 8, 2019
78e1e05
Create tasks only once to enable multiple configuration support
tasomaniac May 8, 2019
bf3eace
Add configured trick to Findbugs separately
tasomaniac May 8, 2019
96c7093
Merge branch 'develop' into taso/lazy-tasks-except-for-ktlint
mr-archano May 12, 2019
c6eb082
Prefer single quote for Strings
tasomaniac May 12, 2019
d18b48e
More clear param name
tasomaniac May 12, 2019
5ae58ca
Reorder methods to better organize
tasomaniac May 12, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -8,14 +8,19 @@ import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.plugins.quality.CodeQualityExtension
import org.gradle.api.tasks.SourceTask
import org.gradle.api.tasks.VerificationTask

abstract class CodeQualityConfigurator<T extends SourceTask, E extends CodeQualityExtension> implements Configurator {
import static com.novoda.staticanalysis.internal.TasksCompat.configureEach
import static com.novoda.staticanalysis.internal.TasksCompat.createTask

abstract class CodeQualityConfigurator<T extends SourceTask & VerificationTask, E extends CodeQualityExtension> implements Configurator {

protected final Project project
protected final Violations violations
protected final Task evaluateViolations
protected final SourceFilter sourceFilter
protected final VariantFilter variantFilter
protected boolean configured = false

protected CodeQualityConfigurator(Project project, Violations violations, Task evaluateViolations) {
this.project = project
Expand All @@ -38,29 +43,57 @@ abstract class CodeQualityConfigurator<T extends SourceTask, E extends CodeQuali
}
project.plugins.withId('com.android.application') {
configureAndroidWithVariants(variantFilter.filteredApplicationVariants)
configureToolTasks()
}
project.plugins.withId('com.android.library') {
configureAndroidWithVariants(variantFilter.filteredLibraryVariants)
configureToolTasks()
}
project.plugins.withId('java') {
configureJavaProject()
configureToolTasks()
}
configureEach(project.tasks.withType(taskClass)) { task ->
configureToolTask(task)
}
}
}

protected void configureJavaProject() {
if (configured) return

project.sourceSets.all { sourceSet ->
def collectViolations = createCollectViolations(getToolTaskNameFor(sourceSet), violations)
evaluateViolations.dependsOn collectViolations
}
configured = true
}

def configureAndroidWithVariants(DomainObjectSet variants) {
variants.all { configureAndroidVariant(it) }
variantFilter.filteredTestVariants.all { configureAndroidVariant(it) }
variantFilter.filteredUnitTestVariants.all { configureAndroidVariant(it) }
protected void configureAndroidWithVariants(DomainObjectSet variants) {
if (configured) return

project.android.sourceSets.all { sourceSet ->
createToolTaskForAndroid(sourceSet)
createCollectViolations(getToolTaskNameFor(sourceSet), violations)
}
variants.all { configureVariant(it) }
variantFilter.filteredTestVariants.all { configureVariant(it) }
variantFilter.filteredUnitTestVariants.all { configureVariant(it) }
configured = true
}

def configureToolTasks() {
project.tasks.withType(taskClass) { task ->
protected void configureVariant(variant) {
def collectViolations = createVariantMetaTask(variant)
evaluateViolations.dependsOn collectViolations
}

private def createVariantMetaTask(variant) {
createTask(project, "collect${getToolTaskNameFor(variant)}VariantViolations", Task) { task ->
task.group = 'verification'
configureReportEvaluation(task, violations)
task.description = "Runs $toolName analysis on all sources for android ${variant.name} variant"
task.mustRunAfter javaCompile(variant)

variant.sourceSets.forEach { sourceSet ->
def toolTaskName = getToolTaskNameFor(sourceSet)
task.dependsOn "collect${toolTaskName.capitalize()}Violations"
}
}
}

Expand All @@ -78,17 +111,29 @@ abstract class CodeQualityConfigurator<T extends SourceTask, E extends CodeQuali
}
}

protected abstract void configureAndroidVariant(variant)

protected void configureJavaProject() {
project.tasks.withType(taskClass) { task ->
sourceFilter.applyTo(task)
task.exclude '**/*.kt'
protected static def javaCompile(variant) {
if (variant.hasProperty('javaCompileProvider')) {
variant.javaCompileProvider.get()
} else {
variant.javaCompile
}
}

protected final String getToolTaskNameFor(sourceSet) {
Copy link
Contributor

Choose a reason for hiding this comment

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

technically this isn't always a sourceset right? maybe we can just called it named?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Soooo, this was actually only for sourceSet. Then I realized that FindBugs needs to be configured per variant because it needs javaCompile task. I will go over and make sure that naming is consistent.

"$toolName${sourceSet.name.capitalize()}"
}

protected abstract Class<T> getTaskClass()

protected abstract void configureReportEvaluation(T task, Violations violations)
protected abstract void createToolTaskForAndroid(sourceSet)

protected void configureToolTask(T task) {
sourceFilter.applyTo(task)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Collected all common functionality here. It is automatically applied to all tool tasks thanks to configureEach in line 53 above.

task.group = 'verification'
task.exclude '**/*.kt'
Copy link
Contributor

Choose a reason for hiding this comment

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

is this ok because this configurator is bound to SourceTask & CodeQualityExtension?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, here exclude method comes from SourceTask.

task.ignoreFailures = true
task.metaClass.getLogger = { QuietLogger.INSTANCE }
}

protected abstract def createCollectViolations(String taskName, Violations violations)
}
@@ -0,0 +1,37 @@
package com.novoda.staticanalysis.internal

import org.gradle.api.Action
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.tasks.TaskCollection
import org.gradle.util.GradleVersion

class TasksCompat {

private static boolean IS_GRADLE_MIN_49 = GradleVersion.current() >= GradleVersion.version("4.9")
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: I'd prefer ' over " 💃


static <T extends Task> Object createTask(Project project, String name, Class<T> type, Action<? super T> configuration) {
if (IS_GRADLE_MIN_49) {
return project.tasks.register(name, type, configuration)
} else {
return project.tasks.create(name, type, configuration)
}
}

static <T extends Task> void configureEach(TaskCollection<T> tasks, Action<? super T> configuration) {
if (IS_GRADLE_MIN_49) {
tasks.configureEach(configuration)
} else {
tasks.all(configuration)
}
}

static <T extends Task> void configureNamed(Project project, String taskName, Action<? super T> configuration) {
if (IS_GRADLE_MIN_49) {
project.tasks.named(taskName).configure(configuration)
} else {
project.tasks.getByName(taskName).configure(configuration)
}
}

}
Expand Up @@ -2,14 +2,15 @@ package com.novoda.staticanalysis.internal.checkstyle

import com.novoda.staticanalysis.Violations
import com.novoda.staticanalysis.internal.CodeQualityConfigurator
import com.novoda.staticanalysis.internal.QuietLogger
import org.gradle.api.Action
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.plugins.quality.Checkstyle
import org.gradle.api.plugins.quality.CheckstyleExtension

import static com.novoda.staticanalysis.internal.TasksCompat.createTask

class CheckstyleConfigurator extends CodeQualityConfigurator<Checkstyle, CheckstyleExtension> {

static CheckstyleConfigurator create(Project project,
Expand Down Expand Up @@ -48,42 +49,27 @@ class CheckstyleConfigurator extends CodeQualityConfigurator<Checkstyle, Checkst
}

@Override
protected void configureAndroidVariant(variant) {
project.with {
variant.sourceSets.each { sourceSet ->
def taskName = "checkstyle${sourceSet.name.capitalize()}"
Checkstyle task = tasks.findByName(taskName)
if (task == null) {
task = tasks.create(taskName, Checkstyle)
task.with {
description = "Run Checkstyle analysis for ${sourceSet.name} classes"
source = sourceSet.java.srcDirs
classpath = files("$buildDir/intermediates/classes/")
exclude '**/*.kt'
}
}
sourceFilter.applyTo(task)
task.mustRunAfter variant.javaCompile
}
protected void createToolTaskForAndroid(sourceSet) {
createTask(project, getToolTaskNameFor(sourceSet), Checkstyle) { task ->
task.description = "Run Checkstyle analysis for ${sourceSet.name} classes"
task.source = sourceSet.java.srcDirs
task.classpath = project.files("${project.buildDir}/intermediates/classes/")
}
}

@Override
protected void configureReportEvaluation(Checkstyle checkstyle, Violations violations) {
checkstyle.showViolations = false
checkstyle.ignoreFailures = true
checkstyle.metaClass.getLogger = { QuietLogger.INSTANCE }

def collectViolations = createCollectViolationsTask(checkstyle, violations)

evaluateViolations.dependsOn collectViolations
collectViolations.dependsOn checkstyle
protected void configureToolTask(Checkstyle task) {
super.configureToolTask(task)
task.showViolations = false
}

private CollectCheckstyleViolationsTask createCollectViolationsTask(Checkstyle checkstyle, Violations violations) {
def task = project.tasks.maybeCreate("collect${checkstyle.name.capitalize()}Violations", CollectCheckstyleViolationsTask)
task.xmlReportFile = checkstyle.reports.xml.destination
task.violations = violations
task
@Override
protected def createCollectViolations(String taskName, Violations violations) {
createTask(project, "collect${taskName.capitalize()}Violations", CollectCheckstyleViolationsTask) { task ->
def checkstyle = project.tasks[taskName] as Checkstyle
task.xmlReportFile = checkstyle.reports.xml.destination
task.violations = violations
task.dependsOn(checkstyle)
}
}
}
Expand Up @@ -9,6 +9,8 @@ import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.Task

import static com.novoda.staticanalysis.internal.TasksCompat.createTask

class DetektConfigurator implements Configurator {

private static final String DETEKT_PLUGIN = 'io.gitlab.arturbosch.detekt'
Expand Down Expand Up @@ -66,7 +68,7 @@ class DetektConfigurator implements Configurator {
}
}

private CollectCheckstyleViolationsTask configureToolTask(detekt) {
private def configureToolTask(detekt) {
def detektTask = project.tasks.findByName('detekt')
if (detektTask?.hasProperty('reports')) {
def reports = detektTask.reports
Expand Down Expand Up @@ -105,13 +107,12 @@ class DetektConfigurator implements Configurator {
}
}

private CollectCheckstyleViolationsTask createCollectViolationsTask(Violations violations, detektTask, File xmlReportFile, File htmlReportFile) {
project.tasks.create('collectDetektViolations', CollectCheckstyleViolationsTask) { task ->
private def createCollectViolationsTask(Violations violations, detektTask, File xmlReportFile, File htmlReportFile) {
createTask(project, 'collectDetektViolations', CollectCheckstyleViolationsTask) { task ->
task.xmlReportFile = xmlReportFile
task.htmlReportFile = htmlReportFile
task.violations = violations

task.dependsOn(detektTask)
task.dependsOn detektTask
}
}

Expand Down