Skip to content

Commit

Permalink
fix detection of android kotlin source directories in AGP >= 7
Browse files Browse the repository at this point in the history
increase maximum gradle version to 8.5
increase maximum ktlint version to 1.1.0
increase maximum kotlin version to 1.9.21
fix lint failures

fixes #702
based on work in #703
  • Loading branch information
wakingrufus committed Dec 20, 2023
1 parent a826dcd commit 6046fb8
Show file tree
Hide file tree
Showing 22 changed files with 256 additions and 52 deletions.
1 change: 1 addition & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ indent_size = 4
ij_kotlin_packages_to_use_import_on_demand = unset
ij_kotlin_name_count_to_use_star_import = 999
ij_kotlin_name_count_to_use_star_import_for_members = 999
ktlint_code_style=intellij_idea

[*.{yml,yaml}]
indent_size = 2
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/).

## [Unreleased]

- fix detection of android kotlin source directories in AGP >= 7 [#733](https://github.com/JLLeitschuh/ktlint-gradle/pull/733)

## [12.0.3] - 2023-12-11

- fix: apply configuration for source sets and targets that are added after the plugin is
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ Minimal supported [Kotlin](https://kotlinlang.org) version: `1.4`

Minimal supported [ktlint](https://github.com/pinterest/ktlint) version: `0.47.1`

Minimal supported [Android Gradle plugin](https://developer.android.com/build) version: `4.1.0`

### Ktlint plugin

#### Simple setup
Expand Down
18 changes: 18 additions & 0 deletions plugin/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
root = true

[*]
charset = utf-8
insert_final_newline = true
trim_trailing_whitespace = true

[*.{kt,kts}]
indent_size = 4
ij_kotlin_packages_to_use_import_on_demand = unset
ij_kotlin_name_count_to_use_star_import = 999
ij_kotlin_name_count_to_use_star_import_for_members = 999
ij_kotlin_allow_trailing_comma = false
ij_kotlin_allow_trailing_comma_on_call_site = false
ktlint_code_style=intellij_idea

[*.{yml,yaml}]
indent_size = 2
2 changes: 1 addition & 1 deletion plugin/VERSION_LATEST_RELEASE.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
11.6.1
12.0.3
9 changes: 8 additions & 1 deletion plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ java {
languageVersion.set(JavaLanguageVersion.of(8))
}
}

ktlint {
version = "1.1.0"
}
tasks.withType<KotlinCompile> {
kotlinOptions {
// target 1.4 as ktlint 0.49 requires it for inline classes
Expand Down Expand Up @@ -195,6 +197,11 @@ tasks.withType<Test> {
maxFailures.set(10)
}
}

// AGP requires java 17, so set it to 17 for tests
javaLauncher = javaToolchains.launcherFor {
languageVersion = JavaLanguageVersion.of(17)
}
}

val relocateShadowJar = tasks.register<ConfigureShadowRelocation>("relocateShadowJar")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ interface ReportersLoaderAdapter<
R,
RP : Serializable,
GR : GenericReporter<R>,
GRP : GenericReporterProvider<GR>> {
GRP : GenericReporterProvider<GR>
> {
fun loadAllReporterProviders(): List<ReporterProviderWrapper<RP>>
fun filterEnabledBuiltInProviders(
enabledReporters: Set<ReporterType>,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@file:Suppress("ConstPropertyName")

package org.jlleitschuh.gradle.ktlint

import org.eclipse.jgit.lib.RepositoryBuilder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ open class KtlintPlugin : Plugin<Project> {
private fun PluginHolder.addKtLintTasksToKotlinPlugin() {
target.plugins.withId("kotlin", applyKtLint())
target.plugins.withId("org.jetbrains.kotlin.js", applyKtLint())
target.plugins.withId("kotlin-android", applyKtLintToAndroid())
target.plugins.withId("org.jetbrains.kotlin.android", applyKtLintToAndroid())
target.plugins.withId(
"org.jetbrains.kotlin.multiplatform",
applyKtlintMultiplatform()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
package org.jlleitschuh.gradle.ktlint.android

import com.android.build.api.dsl.AndroidSourceDirectorySet
import com.android.build.api.dsl.AndroidSourceSet
import com.android.build.api.dsl.BuildFeatures
import com.android.build.api.dsl.BuildType
import com.android.build.api.dsl.CommonExtension
import com.android.build.api.dsl.DefaultConfig
import com.android.build.api.dsl.ProductFlavor
import com.android.build.api.dsl.SigningConfig
import com.android.build.api.variant.Variant
import com.android.build.api.variant.VariantProperties
import com.android.build.gradle.internal.api.DefaultAndroidSourceDirectorySet
import org.gradle.api.Plugin
import org.gradle.api.file.FileCollection
Expand All @@ -21,6 +15,7 @@ import org.jlleitschuh.gradle.ktlint.createGenerateReportsTask
import org.jlleitschuh.gradle.ktlint.setCheckTaskDependsOnGenerateReportsTask
import org.jlleitschuh.gradle.ktlint.tasks.GenerateReportsTask
import java.util.concurrent.Callable
import kotlin.reflect.full.memberProperties

internal fun KtlintPlugin.PluginHolder.applyKtLintToAndroid(): (Plugin<in Any>) -> Unit {
return {
Expand All @@ -43,33 +38,32 @@ internal fun KtlintPlugin.PluginHolder.applyKtLintToAndroid(): (Plugin<in Any>)
}
}

@Suppress("UnstableApiUsage")
private typealias AndroidCommonExtension = CommonExtension<
AndroidSourceSet,
BuildFeatures,
BuildType,
DefaultConfig,
ProductFlavor,
SigningConfig,
Variant<VariantProperties>,
VariantProperties
>

/*
* Variant manager returns all sources for variant,
* so most probably main source set maybe checked several times.
* This approach creates one check tasks per one source set.
*/
@Suppress("UNCHECKED_CAST", "UnstableApiUsage")
@Suppress("UnstableApiUsage")
private fun androidPluginConfigureAction(
pluginHolder: KtlintPlugin.PluginHolder
): (Plugin<Any>) -> Unit = {
pluginHolder.target.extensions.configure(CommonExtension::class.java) {
val androidCommonExtension = this as AndroidCommonExtension

androidCommonExtension.sourceSets.all {
// https://issuetracker.google.com/u/1/issues/170650362
val androidSourceSet = java as DefaultAndroidSourceDirectorySet
// kotlin property exists in AGP >= 7
val kotlinProperty = AndroidSourceSet::class.memberProperties.firstOrNull { it.name == "kotlin" }
if (kotlinProperty == null) {
pluginHolder.target.logger.warn(
buildString {
append("In AGP <7 kotlin source directories are not auto-detected.")
append("In order to lint kotlin sources, manually add the directory to the source set. ")
append("""For example: sourceSets.getByName("main").java.srcDirs("src/main/kotlin/")""")
}
)
}
val sourceMember: AndroidSourceSet.() -> AndroidSourceDirectorySet = {
kotlinProperty?.get(this) as AndroidSourceDirectorySet? ?: this.java
}
sourceSets.all {
val androidSourceSet = sourceMember(this) as DefaultAndroidSourceDirectorySet
// Passing Callable, so returned FileCollection, will lazy evaluate it
// only when task will need it.
// Solves the problem of having additional source dirs in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,8 @@ abstract class GenerateReportsTask @Inject constructor(
internal enum class LintType(
val suffix: String
) {
CHECK("Check"), FORMAT("Format")
CHECK("Check"),
FORMAT("Format")
}

internal companion object {
Expand Down
10 changes: 10 additions & 0 deletions plugin/src/test/kotlin/org/assertj/core/api/Assertions.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.assertj.core.api

import org.assertj.core.internal.Objects
import org.gradle.testkit.runner.BuildTask
import org.gradle.testkit.runner.TaskOutcome

fun ObjectAssert<BuildTask?>.hasOutcome(outcome: TaskOutcome) {
Objects.instance().assertNotNull(this.info, this.actual)
Objects.instance().assertEqual(this.info, this.actual!!.outcome, outcome)
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ abstract class AbstractPluginTest {
GenerateReportsTask.LintType.CHECK
)

protected
fun File.withCleanSources() = createSourceFile(
protected fun File.withCleanSources() = createSourceFile(
"src/main/kotlin/CleanSource.kt",
"""
val foo = "bar"
Expand All @@ -53,12 +52,10 @@ abstract class AbstractPluginTest {
""".trimIndent()
)

protected
fun File.withAlternativeFailingSources(baseDir: String) =
protected fun File.withAlternativeFailingSources(baseDir: String) =
createSourceFile("$baseDir/FailSource.kt", """val foo = "bar"""")

protected
fun File.createSourceFile(sourceFilePath: String, contents: String) {
protected fun File.createSourceFile(sourceFilePath: String, contents: String) {
val sourceFile = resolve(sourceFilePath)
sourceFile.parentFile.mkdirs()
sourceFile.writeText(contents)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses
@AnalyzeClasses(
packages = ["org.jlleitschuh.gradle.ktlint.."]
)
@Suppress("PropertyName")
internal class KtLintClassesUsageScopeTest {
@ArchTest
val `Non-worker plugin classes should not use ktlint classes` = noClasses()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,8 @@ class KtLintSupportedVersionsTest : AbstractPluginTest() {
"0.49.1",
"0.50.0",
"1.0.0",
"1.0.1"
"1.0.1",
"1.1.0"
)

override fun provideArguments(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -677,7 +677,8 @@ class KtlintPluginTest : AbstractPluginTest() {

removeSourceFile(fileOne)
val fileThree = "src/main/kotlin/FileThree.kt"
createSourceFile( // Need to add or modify a source to repro file not found error.
// Need to add or modify a source to repro file not found error.
createSourceFile(
fileThree,
"""
val bar = "foo"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class UnsupportedGradleTest : AbstractPluginTest() {
@Test
@DisabledOnOs(OS.WINDOWS)
internal fun errorOnOldGradleVersion() {
project(GradleVersion.version("6.9.2")) {
project(GradleVersion.version("7.4.1")) {
buildAndFail(CHECK_PARENT_TASK_NAME) {
assertThat(output).contains(
"Current version of plugin supports minimal Gradle version: " +
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package org.jlleitschuh.gradle.ktlint.android

import net.swiftzer.semver.SemVer
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.hasOutcome
import org.gradle.testkit.runner.TaskOutcome
import org.gradle.util.GradleVersion
import org.jlleitschuh.gradle.ktlint.AbstractPluginTest
import org.jlleitschuh.gradle.ktlint.CHECK_PARENT_TASK_NAME
import org.jlleitschuh.gradle.ktlint.testdsl.TestVersions
import org.jlleitschuh.gradle.ktlint.testdsl.androidProjectSetup
import org.jlleitschuh.gradle.ktlint.testdsl.build
import org.jlleitschuh.gradle.ktlint.testdsl.project
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.EnumSource

class KtlintPluginAndroidTest : AbstractPluginTest() {

@ParameterizedTest
@EnumSource(AndroidTestInput::class)
fun `ktlint pass src java`(input: AndroidTestInput) {
project(
input.gradleVersion,
projectSetup = androidProjectSetup(input.agpVersion, input.kotlinVersion, input.ktlintVersion)
) {
withCleanSources("src/main/java/CleanSource.kt")
build(CHECK_PARENT_TASK_NAME) {
assertThat(task(":$mainSourceSetCheckTaskName")).hasOutcome(TaskOutcome.SUCCESS)
assertThat(task(":$kotlinScriptCheckTaskName")).hasOutcome(TaskOutcome.SUCCESS)
assertThat(task(":$CHECK_PARENT_TASK_NAME")).hasOutcome(TaskOutcome.SUCCESS)
}
}
}

@ParameterizedTest
@EnumSource(AndroidTestInput::class)
fun `ktlint pass src kotlin`(input: AndroidTestInput) {
project(
input.gradleVersion,
projectSetup = androidProjectSetup(input.agpVersion, input.kotlinVersion, input.ktlintVersion)
) {
withCleanSources("src/main/kotlin/CleanSource.kt")
if (SemVer.parse(input.agpVersion) < SemVer(7)) {
build(CHECK_PARENT_TASK_NAME) {
assertThat(output).contains("In AGP <7 kotlin source directories are not auto-detected.")
assertThat(task(":$mainSourceSetCheckTaskName")).hasOutcome(TaskOutcome.SKIPPED)
assertThat(task(":$kotlinScriptCheckTaskName")).hasOutcome(TaskOutcome.SUCCESS)
assertThat(task(":$CHECK_PARENT_TASK_NAME")).hasOutcome(TaskOutcome.SUCCESS)
}
} else {
build(CHECK_PARENT_TASK_NAME) {
assertThat(task(":$mainSourceSetCheckTaskName")).hasOutcome(TaskOutcome.SUCCESS)
assertThat(task(":$kotlinScriptCheckTaskName")).hasOutcome(TaskOutcome.SUCCESS)
assertThat(task(":$CHECK_PARENT_TASK_NAME")).hasOutcome(TaskOutcome.SUCCESS)
}
}
}
}

enum class AndroidTestInput(
val gradleVersion: GradleVersion,
val agpVersion: String,
val kotlinVersion: String,
val ktlintVersion: String? = null
) {
MIN(
GradleVersion.version(TestVersions.minSupportedGradleVersion),
TestVersions.minAgpVersion,
// AGP 4.1 requires kotlin 1.5.20
"1.5.20",
// old AGP doesn't properly set variant info which ktlint >= 1 requires
"0.47.1"
),
GRADLE_7_5_AGP_7_4(
// AGP 7.4 requires Gradle 7.5
GradleVersion.version("7.5"),
"7.4.2",
// AGP 4.1 requires kotlin 1.5.20
"1.5.20",
// old AGP doesn't properly set variant info which ktlint >= 1 requires
"0.47.1"
),
MAX_GRADLE_MIN_AGP(
GradleVersion.version(TestVersions.maxSupportedGradleVersion),
TestVersions.minAgpVersion,
// AGP 4.1 requires kotlin 1.5.20
"1.5.20"
),
MAX(
GradleVersion.version(TestVersions.maxSupportedGradleVersion),
TestVersions.maxAgpVersion,
TestVersions.maxSupportedKotlinPluginVersion
)
}
}
Loading

0 comments on commit 6046fb8

Please sign in to comment.