Skip to content

Commit 593a1a3

Browse files
authored
Fix detection of customized SourceSets (#55)
* Fix detection of customized SourceSets Before the plugin only created tasks correctly for source sets built into the convention of the plugin (JVM or Android). Now we use the SourceSetContainer's `all { action }` block to create tasks in a deferred manner after all have been defined. It turns out this can be done without changing the outward behavior of the plugin, types of tasks, or compatibility with older builds. It did require some substantial internal refactoring, mostly to allow for each plugin source set resolver to take an action to apply for each source set discovered which contains Kotlin files. Fixes #53 * rebase and correct version
1 parent 3c4bfc7 commit 593a1a3

File tree

3 files changed

+86
-67
lines changed

3 files changed

+86
-67
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Available on the Gradle Plugins Portal: https://plugins.gradle.org/plugin/org.jm
1010

1111
```groovy
1212
plugins {
13-
id 'org.jmailen.kotlinter' version '1.14.0'
13+
id 'org.jmailen.kotlinter' version '1.15.0'
1414
}
1515
```
1616

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ dependencies {
2121
testRuntime 'com.android.tools.build:gradle:3.0.1'
2222
}
2323

24-
version = '1.14.0'
24+
version = '1.15.0'
2525
group = 'org.jmailen.gradle'
2626
def pluginId = 'org.jmailen.kotlinter'
2727

Lines changed: 84 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,84 @@
11
package org.jmailen.gradle.kotlinter
22

33
import com.android.build.gradle.BaseExtension
4+
import com.android.build.gradle.api.AndroidSourceSet
45
import org.gradle.api.Plugin
56
import org.gradle.api.Project
6-
import org.gradle.api.file.FileCollection
77
import org.gradle.api.file.FileTree
88
import org.gradle.api.file.SourceDirectorySet
99
import org.gradle.api.internal.HasConvention
1010
import org.gradle.api.plugins.JavaPluginConvention
1111
import org.gradle.api.tasks.SourceSet
12+
import org.gradle.api.tasks.SourceSetContainer
1213
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
1314
import org.jmailen.gradle.kotlinter.support.reporterFileExtension
1415
import org.jmailen.gradle.kotlinter.tasks.FormatTask
1516
import org.jmailen.gradle.kotlinter.tasks.LintTask
17+
import java.io.File
1618

1719
class KotlinterPlugin : Plugin<Project> {
1820

1921
val extendablePlugins = mapOf(
20-
"org.jetbrains.kotlin.jvm" to this::kotlinSourceSets,
21-
"kotlin-android" to this::androidSourceSets)
22+
"org.jetbrains.kotlin.jvm" to KotlinJvmSourceSetResolver,
23+
"kotlin-android" to AndroidSourceSetResolver)
2224

2325
override fun apply(project: Project) {
24-
val kotlinterExtention = project.extensions.create("kotlinter", KotlinterExtension::class.java)
26+
val kotlinterExtension = project.extensions.create("kotlinter", KotlinterExtension::class.java)
2527

2628
// for known kotlin plugins, create tasks by convention.
27-
val kotlinApplier = KotlinterApplier(project)
29+
val taskCreator = TaskCreator(project)
2830
extendablePlugins.forEach { pluginId, sourceResolver ->
2931
project.plugins.withId(pluginId) {
30-
val sourceSets = sourceResolver(project)
31-
kotlinApplier.createTasks(sourceSets)
32+
33+
sourceResolver.applyToAll(project) { id, files ->
34+
taskCreator.createSourceSetTasks(id, files)
35+
}
3236
}
3337
}
38+
taskCreator.createParentTasks()
3439

3540
project.afterEvaluate {
36-
kotlinApplier.lintTasks.forEach { lintTask ->
37-
lintTask.ignoreFailures = kotlinterExtention.ignoreFailures
38-
lintTask.indentSize = kotlinterExtention.indentSize
39-
lintTask.continuationIndentSize = kotlinterExtention.continuationIndentSize
40-
lintTask.reports = kotlinterExtention.reporters().associate { reporter ->
41+
taskCreator.lintTasks.forEach { lintTask ->
42+
lintTask.ignoreFailures = kotlinterExtension.ignoreFailures
43+
lintTask.indentSize = kotlinterExtension.indentSize
44+
lintTask.continuationIndentSize = kotlinterExtension.continuationIndentSize
45+
lintTask.reports = kotlinterExtension.reporters().associate { reporter ->
4146
reporter to project.reportFile("${lintTask.sourceSetId}-lint.${reporterFileExtension(reporter)}")
4247
}
4348
}
44-
kotlinApplier.formatTasks.forEach { formatTask ->
45-
formatTask.indentSize = kotlinterExtention.indentSize
46-
formatTask.continuationIndentSize = kotlinterExtention.continuationIndentSize
47-
}
48-
}
49-
}
50-
51-
private fun kotlinSourceSets(project: Project): List<SourceSetInfo> {
52-
return project.sourceSets().map { it.kotlin }.filterNotNull().map {
53-
SourceSetInfo(it.name, it.sourceDirectories)
54-
}
55-
}
56-
57-
private fun androidSourceSets(project: Project): List<SourceSetInfo> {
58-
val android = project.extensions.findByName("android")
59-
val sourceSetInfos = (android as? BaseExtension)?.let {
60-
it.sourceSets.map { androidSourceSet ->
61-
62-
val kotlinSourceTree = androidSourceSet.java.srcDirs.map { dir ->
63-
project.fileTree(dir) {
64-
it.include("**/*.kt")
65-
}
66-
}.reduce { merged: FileTree, tree ->
67-
merged.plus(tree)
68-
}
69-
70-
SourceSetInfo(androidSourceSet.name, kotlinSourceTree)
49+
taskCreator.formatTasks.forEach { formatTask ->
50+
formatTask.indentSize = kotlinterExtension.indentSize
51+
formatTask.continuationIndentSize = kotlinterExtension.continuationIndentSize
7152
}
7253
}
73-
return sourceSetInfos ?: emptyList()
7454
}
75-
76-
private fun Project.sourceSets() = convention.getPlugin(JavaPluginConvention::class.java).sourceSets
77-
78-
private val SourceSet.kotlin: SourceDirectorySet?
79-
get() = ((getConvention("kotlin") ?: getConvention("kotlin2js")) as? KotlinSourceSet)?.kotlin
80-
81-
private fun Any.getConvention(name: String): Any? =
82-
(this as HasConvention).convention.plugins[name]
8355
}
8456

85-
class KotlinterApplier(val project: Project) {
57+
class TaskCreator(private val project: Project) {
8658

8759
val formatTasks = mutableListOf<FormatTask>()
8860
val lintTasks = mutableListOf<LintTask>()
8961

90-
fun createTasks(kotlinSourceSets: List<SourceSetInfo>) {
62+
fun createSourceSetTasks(id: String, files: Set<File>) {
9163

92-
formatTasks += kotlinSourceSets.map { createFormatTask(it) }
64+
formatTasks += project.tasks.create("formatKotlin${id.capitalize()}", FormatTask::class.java) {
65+
it.source(files)
66+
it.report = project.reportFile("$id-format.txt")
67+
}
9368

69+
lintTasks += project.tasks.create("lintKotlin${id.capitalize()}", LintTask::class.java) {
70+
it.source(files)
71+
it.sourceSetId = id
72+
}
73+
}
74+
75+
fun createParentTasks() {
9476
project.tasks.create("formatKotlin") {
9577
it.group = "formatting"
9678
it.description = "Formats the Kotlin source files."
9779
it.dependsOn(formatTasks)
9880
}
9981

100-
lintTasks += kotlinSourceSets.map { createLintTask(it) }
101-
10282
val lintKotlin = project.tasks.create("lintKotlin") {
10383
it.group = "formatting"
10484
it.description = "Runs lint on the Kotlin source files."
@@ -109,25 +89,64 @@ class KotlinterApplier(val project: Project) {
10989
it.dependsOn(lintKotlin)
11090
}
11191
}
92+
}
11293

113-
fun createFormatTask(sourceSet: SourceSetInfo) =
114-
project.tasks.create("formatKotlin${sourceSet.id().capitalize()}", FormatTask::class.java) {
115-
it.source(sourceSet.files())
116-
it.report = project.reportFile("${sourceSet.id()}-format.txt")
117-
}
94+
typealias SourceSetAction = (String, Set<File>) -> Unit
95+
96+
interface SourceSetResolver {
97+
fun applyToAll(project: Project, action: SourceSetAction)
98+
}
99+
100+
object KotlinJvmSourceSetResolver : SourceSetResolver {
118101

119-
fun createLintTask(sourceSet: SourceSetInfo) =
120-
project.tasks.create("lintKotlin${sourceSet.id().capitalize()}", LintTask::class.java) {
121-
it.source(sourceSet.files())
122-
it.sourceSetId = sourceSet.id()
102+
override fun applyToAll(project: Project, action: SourceSetAction) {
103+
getSourceSets(project).all { sourceSet ->
104+
getKotlinFiles(sourceSet)?.let { files ->
105+
action(getSourceSetName(sourceSet)!!.id, files)
123106
}
107+
}
108+
}
109+
110+
private fun getSourceSets(project: Project): SourceSetContainer =
111+
project.convention.getPlugin(JavaPluginConvention::class.java).sourceSets
112+
113+
private fun getSourceSetName(sourceSet: SourceSet) = sourceSet.kotlin?.name
114+
115+
private fun getKotlinFiles(sourceSet: SourceSet) = sourceSet.kotlin?.files
116+
117+
private val SourceSet.kotlin: SourceDirectorySet?
118+
get() = ((getConvention("kotlin") ?: getConvention("kotlin2js")) as? KotlinSourceSet)?.kotlin
119+
120+
private fun SourceSet.getConvention(name: String): Any? =
121+
(this as HasConvention).convention.plugins[name]
124122
}
125123

126-
class SourceSetInfo(val name: String, val sourceDirectories: FileCollection) {
127124

128-
fun id() = name.split(" ").first()
125+
object AndroidSourceSetResolver : SourceSetResolver {
129126

130-
fun files() = sourceDirectories.files
127+
override fun applyToAll(project: Project, action: SourceSetAction) {
128+
val android = project.extensions.findByName("android")
129+
(android as? BaseExtension)?.let {
130+
it.sourceSets.all { sourceSet ->
131+
val id = sourceSet.name.id
132+
val files = getKotlinFiles(project, sourceSet)
133+
if (files.isNotEmpty()) {
134+
action(id, files)
135+
}
136+
}
137+
}
138+
}
139+
140+
private fun getKotlinFiles(project: Project, sourceSet: AndroidSourceSet) = sourceSet.java.srcDirs.map { dir ->
141+
project.fileTree(dir) {
142+
it.include("**/*.kt")
143+
}
144+
}.reduce { merged: FileTree, tree ->
145+
merged.plus(tree)
146+
}.files
131147
}
132148

149+
val String.id
150+
get() = split(" ").first()
151+
133152
fun Project.reportFile(name: String) = file("${project.buildDir}/reports/ktlint/$name")

0 commit comments

Comments
 (0)