Skip to content

Commit

Permalink
Gradle: Use Worker API for detekt & ktlint (#221)
Browse files Browse the repository at this point in the history
  • Loading branch information
vanniktech authored Mar 11, 2022
1 parent 493562c commit 2f8c846
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 50 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ buildscript {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
classpath 'com.vanniktech:gradle-code-quality-tools-plugin:0.20.0'
classpath 'com.vanniktech:gradle-android-junit-jacoco-plugin:0.16.0'
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.16.0'
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.19.0'
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import de.aaschmid.gradle.plugins.cpd.CpdExtension
import de.aaschmid.gradle.plugins.cpd.CpdPlugin
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.attributes.Usage
import org.gradle.api.plugins.quality.Checkstyle
import org.gradle.api.plugins.quality.CheckstyleExtension
import org.gradle.api.plugins.quality.CheckstylePlugin
Expand Down Expand Up @@ -223,20 +224,30 @@ fun Project.addKtlint(rootProject: Project, extension: CodeQualityToolsPluginExt
if (isNotIgnored && isEnabled && isKtlintSupported) {
val ktlint = "ktlint"

configurations.create(ktlint).defaultDependencies {
it.add(dependencies.create("com.pinterest:ktlint:${extension.ktlint.toolVersion}"))
val ktlintConfiguration = configurations.create(ktlint) { configuration ->
configuration.attributes {
it.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage::class.javaObjectType, Usage.JAVA_RUNTIME))
}

configuration.isCanBeConsumed = false

configuration.defaultDependencies {
it.add(dependencies.create("com.pinterest:ktlint:${extension.ktlint.toolVersion}"))
}
}

tasks.register(ktlint, KtLintTask::class.java) { task ->
task.experimental = extension.ktlint.experimental
task.version = extension.ktlint.toolVersion
task.classpath.from(ktlintConfiguration)
task.outputDirectory = File(buildDir, "reports/ktlint/")
task.inputs.files(kotlinFiles(), rootProject.editorconfigFile())
}

tasks.register("ktlintFormat", KtLintFormatTask::class.java) { task ->
task.experimental = extension.ktlint.experimental
task.version = extension.ktlint.toolVersion
task.classpath.from(ktlintConfiguration)
task.outputDirectory = File(buildDir, "reports/ktlint/")
task.inputs.files(kotlinFiles(), rootProject.editorconfigFile())
}
Expand Down Expand Up @@ -290,8 +301,16 @@ fun Project.addDetekt(rootProject: Project, extension: CodeQualityToolsPluginExt
val isDetektSupported = isKotlinProject()

if (isNotIgnored && isEnabled && isDetektSupported) {
configurations.create("detekt").defaultDependencies {
it.add(dependencies.create("io.gitlab.arturbosch.detekt:detekt-cli:${extension.detekt.toolVersion}"))
val detektConfiguration = configurations.create("detekt") { configuration ->
configuration.attributes {
it.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage::class.javaObjectType, Usage.JAVA_RUNTIME))
}

configuration.isCanBeConsumed = false

configuration.defaultDependencies {
it.add(dependencies.create("io.gitlab.arturbosch.detekt:detekt-cli:${extension.detekt.toolVersion}"))
}
}

tasks.register("detektCheck", DetektCheckTask::class.java) { task ->
Expand All @@ -301,7 +320,8 @@ fun Project.addDetekt(rootProject: Project, extension: CodeQualityToolsPluginExt
task.version = extension.detekt.toolVersion
task.outputDirectory = File(buildDir, "reports/detekt/")
task.configFile = rootProject.file(extension.detekt.config)
task.input = extension.detekt.input
task.inputFile = file(extension.detekt.input)
task.classpath.from(detektConfiguration)
task.inputs.files(kotlinFiles(baseDir = extension.detekt.input))

task.inputs.property("baseline-file-exists", false)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,34 @@
package com.vanniktech.code.quality.tools

import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.file.FileCollection
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.Classpath
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.PathSensitive
import org.gradle.api.tasks.PathSensitivity.RELATIVE
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.TaskExecutionException
import org.gradle.process.ExecOperations
import org.gradle.workers.WorkAction
import org.gradle.workers.WorkParameters
import org.gradle.workers.WorkerExecutionException
import org.gradle.workers.WorkerExecutor
import java.io.File
import javax.inject.Inject

@CacheableTask open class DetektCheckTask : DefaultTask() {
@Input var input: String = "."
@CacheableTask abstract class DetektCheckTask : DefaultTask() {
@InputDirectory @PathSensitive(RELATIVE) lateinit var inputFile: File
@Input var failFast: Boolean = true
@Input var buildUponDefaultConfig: Boolean = false
@Input var parallel: Boolean = false
@Input lateinit var version: String
@get:Classpath abstract val classpath: ConfigurableFileCollection

// Ideally this would be an optional input file - https://github.com/gradle/gradle/issues/2016
@Input @Optional var baselineFilePath: String? = null
Expand All @@ -31,68 +40,99 @@ import java.io.File
description = "Runs detekt."
}

@get:Inject internal abstract val workerExecutor: WorkerExecutor

@TaskAction fun run() {
val configuration = project.configurations.getByName("detekt")
val queue = workerExecutor.noIsolation()

queue.submit(DetektWorker::class.java) {
it.baselineFilePath.set(baselineFilePath)
it.buildUponDefaultConfig.set(buildUponDefaultConfig)
it.classpath.from(classpath)
it.configFile.set(configFile)
it.failFast.set(failFast)
it.inputFile.set(inputFile)
it.outputDirectory.set(outputDirectory)
it.parallel.set(parallel)
}
}
}

baselineFilePath?.let { file ->
internal data class ReportingMetaInformation(
val reportId: String,
val fileEnding: String,
val fileNameSuffix: String
)

internal interface DetektParameters : WorkParameters {
val baselineFilePath: Property<String?>
val buildUponDefaultConfig: Property<Boolean>
val classpath: ConfigurableFileCollection
val configFile: RegularFileProperty
val failFast: Property<Boolean>
val inputFile: RegularFileProperty
val outputDirectory: RegularFileProperty
val parallel: Property<Boolean>
}

internal abstract class DetektWorker @Inject internal constructor(
private val execOperations: ExecOperations,
) : WorkAction<DetektParameters> {
override fun execute() {
parameters.baselineFilePath.orNull?.let { file ->
if (!File(file).exists()) {
executeDetekt(configuration, shouldCreateBaseLine = true)
throw TaskExecutionException(this, GradleException("Aborting build since new baseline file was created"))
executeDetekt(shouldCreateBaseLine = true)
throw WorkerExecutionException("Aborting build since new baseline file was created")
}
}

executeDetekt(configuration)
executeDetekt()
}

private fun executeDetekt(configuration: FileCollection, shouldCreateBaseLine: Boolean = false) {
private fun executeDetekt(shouldCreateBaseLine: Boolean = false) {
val reportKey = "--report"
val reportValue = listOf(
ReportingMetaInformation("plain", "txt", "plain"),
ReportingMetaInformation("xml", "xml", "checkstyle"),
ReportingMetaInformation("html", "html", "report")
).joinToString(separator = ",") {
it.fileEnding + ":" + File(outputDirectory, "detekt-${it.fileNameSuffix}.${it.fileEnding}").absolutePath
it.fileEnding + ":" + File(parameters.outputDirectory.asFile.get(), "detekt-${it.fileNameSuffix}.${it.fileEnding}").absolutePath
}

project.javaexec { task ->
task.main = "io.gitlab.arturbosch.detekt.cli.Main"
task.classpath = configuration
execOperations.javaexec { task ->
task.mainClass.set("io.gitlab.arturbosch.detekt.cli.Main")
task.classpath = parameters.classpath
task.args(
"--input", project.file(input),
"--input", parameters.inputFile.get().asFile,
reportKey, reportValue
)

val configFile = parameters.configFile.asFile.get()
if (configFile.exists()) {
task.args("--config", configFile)
}

task.args("--excludes", "**/build/**")

if (failFast) {
if (parameters.failFast.get()) {
task.args("--fail-fast")
}

if (buildUponDefaultConfig) {
if (parameters.buildUponDefaultConfig.get()) {
task.args("--build-upon-default-config")
}

if (parallel) {
if (parameters.parallel.get()) {
task.args("--parallel")
}

if (shouldCreateBaseLine) {
task.args("--create-baseline")
}

baselineFilePath?.let {
parameters.baselineFilePath.orNull?.let {
task.args("--baseline", it)
}
}
}

internal data class ReportingMetaInformation(
val reportId: String,
val fileEnding: String,
val fileNameSuffix: String
)
}
Original file line number Diff line number Diff line change
@@ -1,37 +1,71 @@
package com.vanniktech.code.quality.tools

import org.gradle.api.DefaultTask
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.Classpath
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
import org.gradle.process.ExecOperations
import org.gradle.workers.WorkAction
import org.gradle.workers.WorkParameters
import org.gradle.workers.WorkerExecutor
import java.io.File
import javax.inject.Inject

@CacheableTask open class KtLintFormatTask : DefaultTask() {
@CacheableTask abstract class KtLintFormatTask : DefaultTask() {
@Input var experimental: Boolean = false
@Input lateinit var version: String
@get:Classpath abstract val classpath: ConfigurableFileCollection
@OutputDirectory lateinit var outputDirectory: File

init {
group = "formatting"
description = "Runs ktlint and autoformats your code."
}

@get:Inject internal abstract val workerExecutor: WorkerExecutor

@TaskAction fun run() {
project.javaexec { task ->
val queue = workerExecutor.noIsolation()

queue.submit(KtLintFormatWorker::class.java) {
it.classpath.from(classpath)
it.experimental.set(experimental)
it.outputDirectory.set(outputDirectory)
}
}
}

internal interface KtLintFormatParameters : WorkParameters {
val classpath: ConfigurableFileCollection
val experimental: Property<Boolean>
val outputDirectory: RegularFileProperty
}

internal abstract class KtLintFormatWorker @Inject internal constructor(
private val execOperations: ExecOperations,
) : WorkAction<KtLintFormatParameters> {
override fun execute() {
execOperations.javaexec { task ->
task.mainClass.set("com.pinterest.ktlint.Main")
task.classpath = project.configurations.getByName("ktlint")
if (experimental) {
task.classpath = parameters.classpath

if (parameters.experimental.get()) {
task.args("--experimental")
}

task.args(
"--reporter=plain",
"--reporter=checkstyle,output=${File(outputDirectory, "ktlint-checkstyle-report.xml")}",
"--reporter=checkstyle,output=${File(parameters.outputDirectory.asFile.get(), "ktlint-checkstyle-report.xml")}",
"-F",
"**/*.kt",
"**/*.kts",
"!build/",
"!build/**"
"!build/**",
)
}
}
Expand Down
44 changes: 39 additions & 5 deletions src/main/kotlin/com/vanniktech/code/quality/tools/KtLintTask.kt
Original file line number Diff line number Diff line change
@@ -1,32 +1,66 @@
package com.vanniktech.code.quality.tools

import org.gradle.api.DefaultTask
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.Classpath
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
import org.gradle.process.ExecOperations
import org.gradle.workers.WorkAction
import org.gradle.workers.WorkParameters
import org.gradle.workers.WorkerExecutor
import java.io.File
import javax.inject.Inject

@CacheableTask open class KtLintTask : DefaultTask() {
@CacheableTask abstract class KtLintTask : DefaultTask() {
@Input var experimental: Boolean = false
@Input lateinit var version: String
@get:Classpath abstract val classpath: ConfigurableFileCollection
@OutputDirectory lateinit var outputDirectory: File

init {
group = "verification"
description = "Runs ktlint."
}

@get:Inject internal abstract val workerExecutor: WorkerExecutor

@TaskAction fun run() {
project.javaexec { task ->
val queue = workerExecutor.noIsolation()

queue.submit(KtLintWorker::class.java) {
it.classpath.from(classpath)
it.experimental.set(experimental)
it.outputDirectory.set(outputDirectory)
}
}
}

internal interface KtLintParameters : WorkParameters {
val classpath: ConfigurableFileCollection
val experimental: Property<Boolean>
val outputDirectory: RegularFileProperty
}

internal abstract class KtLintWorker @Inject internal constructor(
private val execOperations: ExecOperations,
) : WorkAction<KtLintParameters> {
override fun execute() {
execOperations.javaexec { task ->
task.mainClass.set("com.pinterest.ktlint.Main")
task.classpath = project.configurations.getByName("ktlint")
if (experimental) {
task.classpath = parameters.classpath

if (parameters.experimental.get()) {
task.args("--experimental")
}

task.args(
"--reporter=plain",
"--reporter=checkstyle,output=${File(outputDirectory, "ktlint-checkstyle-report.xml")}",
"--reporter=checkstyle,output=${File(parameters.outputDirectory.asFile.get(), "ktlint-checkstyle-report.xml")}",
"**/*.kt",
"**/*.kts",
"!build/",
Expand Down
Loading

0 comments on commit 2f8c846

Please sign in to comment.