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

Make LintTask incremental #289

Open
wants to merge 3 commits into
base: run_lint_with_process_isolation
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
@@ -1,31 +1,39 @@
package org.jmailen.gradle.kotlinter.tasks

import groovy.lang.Closure
import org.gradle.api.DefaultTask
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.FileCollection
import org.gradle.api.file.FileTreeElement
import org.gradle.api.file.ProjectLayout
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.MapProperty
import org.gradle.api.provider.Property
import org.gradle.api.specs.Spec
import org.gradle.api.tasks.Classpath
import org.gradle.api.tasks.IgnoreEmptyDirectories
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.PathSensitive
import org.gradle.api.tasks.PathSensitivity
import org.gradle.api.tasks.SourceTask
import org.gradle.api.tasks.SkipWhenEmpty
import org.gradle.api.tasks.util.PatternFilterable
import org.gradle.api.tasks.util.PatternSet
import org.gradle.work.FileChange
import org.gradle.work.Incremental
import org.gradle.work.InputChanges
import org.jmailen.gradle.kotlinter.KotlinterExtension.Companion.DEFAULT_DISABLED_RULES
import org.jmailen.gradle.kotlinter.KotlinterExtension.Companion.DEFAULT_EXPERIMENTAL_RULES
import org.jmailen.gradle.kotlinter.support.KtLintParams
import org.jmailen.gradle.kotlinter.support.findApplicableEditorConfigFiles
import java.util.concurrent.Callable

abstract class ConfigurableKtLintTask(
projectLayout: ProjectLayout,
objectFactory: ObjectFactory,
) : SourceTask() {
) : DefaultTask(), PatternFilterable {

@Input
val experimentalRules: Property<Boolean> = objectFactory.property(default = DEFAULT_EXPERIMENTAL_RULES)
Expand All @@ -49,18 +57,46 @@ abstract class ConfigurableKtLintTask(
@Classpath
val ruleSetsClasspath: ConfigurableFileCollection = objectFactory.fileCollection()

private val allSourceFiles = project.objects.fileCollection()

@get:Internal
internal val patternFilterable: PatternFilterable = PatternSet()

@SkipWhenEmpty // Marks the input incremental: https://github.com/gradle/gradle/issues/17593
@InputFiles
@PathSensitive(PathSensitivity.RELATIVE)
@IgnoreEmptyDirectories
val source: FileCollection = objectFactory.fileCollection()
.from(Callable { allSourceFiles.asFileTree.matching(patternFilterable) })

fun source(vararg sources: Any?) = also { allSourceFiles.setFrom(*sources) }

fun setSource(source: Any) {
allSourceFiles.setFrom(source)
}

@Internal
protected fun getKtLintParams(): KtLintParams = KtLintParams(
experimentalRules = experimentalRules.get(),
disabledRules = disabledRules.get(),
)

protected fun getChangedEditorconfigFiles(inputChanges: InputChanges) =
if (inputChanges.isIncremental) {
inputChanges.getFileChanges(editorconfigFiles).map(FileChange::getFile)
} else {
emptyList()
}
@Internal
override fun getIncludes(): MutableSet<String> = patternFilterable.includes

@Internal
override fun getExcludes(): MutableSet<String> = patternFilterable.excludes

override fun setIncludes(includes: MutableIterable<String>) = also { patternFilterable.setIncludes(includes) }
override fun setExcludes(excludes: MutableIterable<String>) = also { patternFilterable.setExcludes(excludes) }
override fun include(vararg includes: String?) = also { patternFilterable.include(*includes) }
override fun include(includes: MutableIterable<String>) = also { patternFilterable.include(includes) }
override fun include(includeSpec: Spec<FileTreeElement>) = also { patternFilterable.include(includeSpec) }
override fun include(includeSpec: Closure<*>) = also { patternFilterable.include(includeSpec) }
override fun exclude(vararg excludes: String?) = also { patternFilterable.exclude(*excludes) }
override fun exclude(excludes: MutableIterable<String>) = also { patternFilterable.exclude(excludes) }
override fun exclude(excludeSpec: Spec<FileTreeElement>) = also { patternFilterable.exclude(excludeSpec) }
override fun exclude(excludeSpec: Closure<*>) = also { patternFilterable.exclude(excludeSpec) }
}

internal inline fun <reified T> ObjectFactory.property(default: T? = null): Property<T> =
Expand All @@ -77,3 +113,17 @@ internal inline fun <reified K, reified V> ObjectFactory.mapProperty(default: Ma
mapProperty(K::class.java, V::class.java).apply {
set(default)
}

internal fun ConfigurableKtLintTask.getChangedEditorconfigFiles(inputChanges: InputChanges) =
if (inputChanges.isIncremental) {
inputChanges.getFileChanges(editorconfigFiles).map(FileChange::getFile)
} else {
emptyList()
}

internal fun ConfigurableKtLintTask.getChangedSources(inputChanges: InputChanges) =
if (inputChanges.isIncremental && inputChanges.getFileChanges(editorconfigFiles).none()) {
inputChanges.getFileChanges(source).map(FileChange::getFile)
} else {
source
}
10 changes: 1 addition & 9 deletions src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/LintTask.kt
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
package org.jmailen.gradle.kotlinter.tasks

import org.gradle.api.file.FileTree
import org.gradle.api.file.ProjectLayout
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.MapProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.OutputFiles
import org.gradle.api.tasks.PathSensitive
import org.gradle.api.tasks.PathSensitivity
import org.gradle.api.tasks.TaskAction
import org.gradle.work.InputChanges
import org.gradle.workers.WorkerExecutor
Expand All @@ -32,10 +28,6 @@ open class LintTask @Inject constructor(
@OutputFiles
val reports: MapProperty<String, File> = objectFactory.mapProperty(default = emptyMap())

@InputFiles
@PathSensitive(PathSensitivity.RELATIVE)
override fun getSource(): FileTree = super.getSource()

@Input
val ignoreFailures: Property<Boolean> = objectFactory.property(default = DEFAULT_IGNORE_FAILURES)

Expand All @@ -50,7 +42,7 @@ open class LintTask @Inject constructor(

workQueue.submit(LintWorkerAction::class.java) { p ->
p.name.set(name)
p.files.from(source)
p.files.from(getChangedSources(inputChanges))
p.projectDirectory.set(projectLayout.projectDirectory.asFile)
p.reporters.putAll(reports)
p.ktLintParams.set(getKtLintParams())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ abstract class FormatWorkerAction : WorkAction<FormatWorkerParameters> {
changedEditorconfigFiles = parameters.changedEditorConfigFiles,
logger = logger,
)
logger.info("Resolved ${ktLintEngine.ruleProviders.size} RuleProviders")
logger.info("$name - resolved ${ktLintEngine.ruleProviders.size} RuleProviders")
logger.info("$name - executing against ${files.size} file(s)")

val fixes = mutableListOf<String>()
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ abstract class LintWorkerAction : WorkAction<LintWorkerParameters> {
changedEditorconfigFiles = parameters.changedEditorConfigFiles,
logger = logger,
)
logger.info("Resolved ${ktLintEngine.ruleProviders.size} RuleProviders")
logger.info("$name - resolved ${ktLintEngine.ruleProviders.size} RuleProviders")
logger.info("$name - executing against ${files.size} file(s)")
if (logger.isDebugEnabled) {
logger.debug("Resolved RuleSetProviders = ${ktLintEngine.ruleProviders.joinToString { it.createNewRuleInstance().id }}")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.jmailen.gradle.kotlinter.functional

import org.gradle.testkit.runner.TaskOutcome.FAILED
import org.gradle.testkit.runner.TaskOutcome.NO_SOURCE
import org.gradle.testkit.runner.TaskOutcome.SUCCESS
import org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE
import org.jmailen.gradle.kotlinter.functional.utils.editorConfig
Expand Down Expand Up @@ -193,6 +194,66 @@ internal class KotlinProjectTest : WithGradleTest.Kotlin() {
}
}

@Test
fun `lint task is incremental`() {
settingsFile()
buildFile()
editorConfig()
kotlinSourceFile(
"CustomObject.kt",
"""
object CustomObject

""".trimIndent(),
)
kotlinSourceFile(
"CustomClass.kt",
"""
data class CustomClass(val value: Int)

""".trimIndent(),
)

build("lintKotlin", "--info").apply {
assertEquals(SUCCESS, task(":lintKotlin")?.outcome)
assertEquals(SUCCESS, task(":lintKotlinMain")?.outcome)
assertEquals(NO_SOURCE, task(":lintKotlinTest")?.outcome)
assertTrue(output.contains("lintKotlinMain - executing against 2 file(s)"))
}

kotlinSourceFile(
"CustomClass.kt",
"""
data class CustomClass(val modified: Int)

""".trimIndent(),
)
build("lintKotlin", "--info").apply {
assertEquals(SUCCESS, task(":lintKotlin")?.outcome)
assertEquals(SUCCESS, task(":lintKotlinMain")?.outcome)
assertEquals(NO_SOURCE, task(":lintKotlinTest")?.outcome)
assertTrue(output.contains("lintKotlinMain - executing against 1 file(s)"))
}

editorconfigFile.appendText("content=updated")
build("lintKotlin", "--info").apply {
assertEquals(SUCCESS, task(":lintKotlin")?.outcome)
assertTrue(output.contains("lintKotlinMain - executing against 2 file(s)"))
}

kotlinSourceFile(
"CustomClass.kt",
"""
data class CustomClass(val modifiedEditorconfig: Int)

""".trimIndent(),
)
build("lintKotlin", "--info").apply {
assertEquals(SUCCESS, task(":lintKotlin")?.outcome)
assertTrue(output.contains("lintKotlinMain - executing against 1 file(s)"))
}
}

@Test
fun `plugin is compatible with configuration cache`() {
settingsFile()
Expand Down Expand Up @@ -225,7 +286,7 @@ internal class KotlinProjectTest : WithGradleTest.Kotlin() {
}

@Test
fun `plugin resolves dynamically loaded RulesetProviders`() {
fun `plugin resolves dynamically loaded RuleSetProviders`() {
settingsFile()
buildFile()
editorConfig()
Expand All @@ -237,18 +298,22 @@ internal class KotlinProjectTest : WithGradleTest.Kotlin() {
""".trimIndent(),
)

val matcher = "Resolved (\\d+) RuleProviders".toRegex()
fun String.findResolvedRuleProvidersCount(taskName: String): Int {
val matcher = "$taskName - resolved (\\d+) RuleProviders".toRegex()

return matcher.find(this)?.groups?.get(1)?.value?.toIntOrNull() ?: 0
}

build("lintKotlin", "--info").apply {
assertEquals(SUCCESS, task(":lintKotlin")?.outcome)
val resolvedRulesCount = matcher.find(output)?.groups?.get(1)?.value?.toIntOrNull() ?: 0
assertTrue(resolvedRulesCount > 0)
val resolvedRulesCount = output.findResolvedRuleProvidersCount("lintKotlinMain")
assertTrue(resolvedRulesCount > 0) { "expected to find more than 0 resolved RuleProviders, was=$resolvedRulesCount" }
}

build("formatKotlin", "--info").apply {
assertEquals(SUCCESS, task(":formatKotlin")?.outcome)
val resolvedRulesCount = matcher.find(output)?.groups?.get(1)?.value?.toIntOrNull() ?: 0
assertTrue(resolvedRulesCount > 0)
val resolvedRulesCount = output.findResolvedRuleProvidersCount("formatKotlinMain")
assertTrue(resolvedRulesCount > 0) { "expected to find more than 0 resolved RuleProviders, was=$resolvedRulesCount" }
}
}

Expand Down