diff --git a/.editorconfig b/.editorconfig index bc7c4ad4..07ebe96f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -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 diff --git a/.github/workflows/build-and-check.yml b/.github/workflows/build-and-check.yml index 28fa6cec..78141739 100644 --- a/.github/workflows/build-and-check.yml +++ b/.github/workflows/build-and-check.yml @@ -103,6 +103,8 @@ jobs: distribution: 'zulu' - name: Install lintian run: sudo apt install -qq lintian=2.114.0ubuntu1.3 + - name: Install libdistro + run: sudo apt install -qq libdistro-info-perl=1.1ubuntu0.2 - name: Install checkbashisms run: sudo apt-get install -qq devscripts - name: Restore Gradle caches diff --git a/CHANGELOG.md b/CHANGELOG.md index 518e8b37..7447f2ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/README.md b/README.md index 997912dd..a2361d22 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/plugin/.editorconfig b/plugin/.editorconfig new file mode 100644 index 00000000..c618828a --- /dev/null +++ b/plugin/.editorconfig @@ -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 diff --git a/plugin/VERSION_LATEST_RELEASE.txt b/plugin/VERSION_LATEST_RELEASE.txt index 1a31377d..470ce40f 100644 --- a/plugin/VERSION_LATEST_RELEASE.txt +++ b/plugin/VERSION_LATEST_RELEASE.txt @@ -1 +1 @@ -11.6.1 +12.0.3 diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index eab4e7f6..edad8b1d 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -30,6 +30,10 @@ java { } } +ktlint { + version = "1.1.0" +} + tasks.withType { kotlinOptions { // target 1.4 as ktlint 0.49 requires it for inline classes @@ -56,6 +60,7 @@ configurations { } configurations["compileOnly"].extendsFrom(shadowImplementation) configurations["testImplementation"].extendsFrom(shadowImplementation) + sourceSets { val adapter by creating { } @@ -88,6 +93,7 @@ sourceSets { runtimeClasspath = adapters.map { it.output }.fold(runtimeClasspath) { a, b -> a + b } } } + val test by getting { kotlin { compileClasspath = adapters.map { it.output }.fold(compileClasspath) { a, b -> a + b } @@ -95,6 +101,7 @@ sourceSets { } } } + val adapterSources = listOf( sourceSets.named("adapter"), sourceSets.named("adapter47"), @@ -140,12 +147,6 @@ dependencies { // Explicitly added for shadow plugin to relocate implementation as well shadowImplementation(libs.slf4j.nop) - /* - * Do not depend upon the gradle script kotlin plugin API. IE: gradleScriptKotlinApi() - * It's currently in flux and has binary breaking changes in gradle 4.0 - * https://github.com/JLLeitschuh/ktlint-gradle/issues/9 - */ - testImplementation(libs.junit.jupiter) testImplementation(libs.assertj.core) testImplementation(libs.kotlin.reflect) @@ -165,12 +166,8 @@ kotlin { } } -// Test tasks loods plugin from local maven repository -tasks.named("test") { - dependsOn("publishToMavenLocal") -} - tasks.withType { + dependsOn("publishToMavenLocal") useJUnitPlatform() maxParallelForks = (Runtime.getRuntime().availableProcessors() / 2).takeIf { it > 0 } ?: 1 doFirst { @@ -195,6 +192,12 @@ tasks.withType { maxFailures.set(10) } } + + javaLauncher.set( + javaToolchains.launcherFor { + languageVersion = JavaLanguageVersion.of(JavaVersion.current().majorVersion) + } + ) } val relocateShadowJar = tasks.register("relocateShadowJar") @@ -273,7 +276,9 @@ fun setupPublishingEnvironment() { if (System.getProperty(keyProperty) == null || System.getProperty(secretProperty) == null) { logger - .info("`$keyProperty` or `$secretProperty` were not set. Attempting to configure from environment variables") + .info( + "`$keyProperty` or `$secretProperty` were not set. Attempting to configure from environment variables" + ) val key: String? = System.getenv(keyEnvironmentVariable) val secret: String? = System.getenv(secretEnvironmentVariable) diff --git a/plugin/src/adapter/kotlin/org/jlleitschuh/gradle/ktlint/reporter/ReportersLoaderAdapter.kt b/plugin/src/adapter/kotlin/org/jlleitschuh/gradle/ktlint/reporter/ReportersLoaderAdapter.kt index b7cdf354..5bc20c10 100644 --- a/plugin/src/adapter/kotlin/org/jlleitschuh/gradle/ktlint/reporter/ReportersLoaderAdapter.kt +++ b/plugin/src/adapter/kotlin/org/jlleitschuh/gradle/ktlint/reporter/ReportersLoaderAdapter.kt @@ -12,7 +12,8 @@ interface ReportersLoaderAdapter< R, RP : Serializable, GR : GenericReporter, - GRP : GenericReporterProvider> { + GRP : GenericReporterProvider + > { fun loadAllReporterProviders(): List> fun filterEnabledBuiltInProviders( enabledReporters: Set, diff --git a/plugin/src/main/kotlin/org/jlleitschuh/gradle/ktlint/GitHook.kt b/plugin/src/main/kotlin/org/jlleitschuh/gradle/ktlint/GitHook.kt index af168342..d43c825a 100644 --- a/plugin/src/main/kotlin/org/jlleitschuh/gradle/ktlint/GitHook.kt +++ b/plugin/src/main/kotlin/org/jlleitschuh/gradle/ktlint/GitHook.kt @@ -1,3 +1,5 @@ +@file:Suppress("ConstPropertyName") + package org.jlleitschuh.gradle.ktlint import org.eclipse.jgit.lib.RepositoryBuilder diff --git a/plugin/src/main/kotlin/org/jlleitschuh/gradle/ktlint/KtlintPlugin.kt b/plugin/src/main/kotlin/org/jlleitschuh/gradle/ktlint/KtlintPlugin.kt index b5151ec9..8699412c 100644 --- a/plugin/src/main/kotlin/org/jlleitschuh/gradle/ktlint/KtlintPlugin.kt +++ b/plugin/src/main/kotlin/org/jlleitschuh/gradle/ktlint/KtlintPlugin.kt @@ -29,7 +29,7 @@ open class KtlintPlugin : Plugin { 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() diff --git a/plugin/src/main/kotlin/org/jlleitschuh/gradle/ktlint/android/AndroidPluginsApplier.kt b/plugin/src/main/kotlin/org/jlleitschuh/gradle/ktlint/android/AndroidPluginsApplier.kt index c8c2d7ec..ac9bbfb5 100644 --- a/plugin/src/main/kotlin/org/jlleitschuh/gradle/ktlint/android/AndroidPluginsApplier.kt +++ b/plugin/src/main/kotlin/org/jlleitschuh/gradle/ktlint/android/AndroidPluginsApplier.kt @@ -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 @@ -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) -> Unit { return { @@ -43,33 +38,32 @@ internal fun KtlintPlugin.PluginHolder.applyKtLintToAndroid(): (Plugin) } } -@Suppress("UnstableApiUsage") -private typealias AndroidCommonExtension = CommonExtension< - AndroidSourceSet, - BuildFeatures, - BuildType, - DefaultConfig, - ProductFlavor, - SigningConfig, - Variant, - 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) -> 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 diff --git a/plugin/src/main/kotlin/org/jlleitschuh/gradle/ktlint/tasks/GenerateReportsTask.kt b/plugin/src/main/kotlin/org/jlleitschuh/gradle/ktlint/tasks/GenerateReportsTask.kt index 75077060..d054a103 100644 --- a/plugin/src/main/kotlin/org/jlleitschuh/gradle/ktlint/tasks/GenerateReportsTask.kt +++ b/plugin/src/main/kotlin/org/jlleitschuh/gradle/ktlint/tasks/GenerateReportsTask.kt @@ -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 { diff --git a/plugin/src/test/kotlin/org/assertj/core/api/Assertions.kt b/plugin/src/test/kotlin/org/assertj/core/api/Assertions.kt new file mode 100644 index 00000000..a6faaf3f --- /dev/null +++ b/plugin/src/test/kotlin/org/assertj/core/api/Assertions.kt @@ -0,0 +1,9 @@ +package org.assertj.core.api + +import org.gradle.testkit.runner.BuildTask +import org.gradle.testkit.runner.TaskOutcome + +fun ObjectAssert.hasOutcome(outcome: TaskOutcome) { + this.objects.assertNotNull(this.info, this.actual) + this.objects.assertEqual(this.info, this.actual!!.outcome, outcome) +} diff --git a/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/AbstractPluginTest.kt b/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/AbstractPluginTest.kt index d46f4211..df8d3375 100644 --- a/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/AbstractPluginTest.kt +++ b/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/AbstractPluginTest.kt @@ -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" @@ -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) diff --git a/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/KtLintClassesUsageScopeTest.kt b/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/KtLintClassesUsageScopeTest.kt index 9f76c069..8c12ca17 100644 --- a/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/KtLintClassesUsageScopeTest.kt +++ b/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/KtLintClassesUsageScopeTest.kt @@ -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() diff --git a/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/KtLintSupportedVersionsTest.kt b/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/KtLintSupportedVersionsTest.kt index 9ecd938e..af283e69 100644 --- a/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/KtLintSupportedVersionsTest.kt +++ b/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/KtLintSupportedVersionsTest.kt @@ -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( diff --git a/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/KtlintPluginTest.kt b/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/KtlintPluginTest.kt index 7784b568..4e0c62d8 100644 --- a/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/KtlintPluginTest.kt +++ b/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/KtlintPluginTest.kt @@ -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" diff --git a/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/UnsupportedGradleTest.kt b/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/UnsupportedGradleTest.kt index 13913896..112e11fa 100644 --- a/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/UnsupportedGradleTest.kt +++ b/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/UnsupportedGradleTest.kt @@ -3,7 +3,9 @@ package org.jlleitschuh.gradle.ktlint import org.assertj.core.api.Assertions.assertThat import org.gradle.util.GradleVersion import org.jlleitschuh.gradle.ktlint.testdsl.buildAndFail +import org.jlleitschuh.gradle.ktlint.testdsl.getMajorJavaVersion import org.jlleitschuh.gradle.ktlint.testdsl.project +import org.junit.jupiter.api.Assumptions import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import org.junit.jupiter.api.condition.DisabledOnOs @@ -14,7 +16,15 @@ class UnsupportedGradleTest : AbstractPluginTest() { @Test @DisabledOnOs(OS.WINDOWS) internal fun errorOnOldGradleVersion() { - project(GradleVersion.version("6.9.2")) { + /** + * This test ensures the proper error message is printed when an unsupported version of gradle is used. + * However, our minimum version of gradle is still 7.x, which will not run at all on Java 21. + * Gradle 8.5 is needed for Java 21. + * So if java 21 is currently being used, skip this test + */ + Assumptions.assumeFalse(getMajorJavaVersion() >= 21) + + project(GradleVersion.version("7.4.1")) { buildAndFail(CHECK_PARENT_TASK_NAME) { assertThat(output).contains( "Current version of plugin supports minimal Gradle version: " + diff --git a/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/android/KtlintPluginAndroidTest.kt b/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/android/KtlintPluginAndroidTest.kt new file mode 100644 index 00000000..bb37b6c0 --- /dev/null +++ b/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/android/KtlintPluginAndroidTest.kt @@ -0,0 +1,107 @@ +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.getMajorJavaVersion +import org.jlleitschuh.gradle.ktlint.testdsl.project +import org.junit.jupiter.api.Assumptions.assumeFalse +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) { + assumeFalse(input.minimumJava != null && getMajorJavaVersion() < input.minimumJava) + assumeFalse(input.maximumJava != null && getMajorJavaVersion() > input.maximumJava) + 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) { + assumeFalse(input.minimumJava != null && getMajorJavaVersion() < input.minimumJava) + assumeFalse(input.maximumJava != null && getMajorJavaVersion() > input.maximumJava) + 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, + val minimumJava: Int? = null, + val maximumJava: Int? = 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", + maximumJava = 17 + ), + 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", + minimumJava = 11, + maximumJava = 17 + ), + 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, + minimumJava = 17 + ) + } +} diff --git a/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/testdsl/AndroidSetup.kt b/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/testdsl/AndroidSetup.kt new file mode 100644 index 00000000..21798489 --- /dev/null +++ b/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/testdsl/AndroidSetup.kt @@ -0,0 +1,76 @@ +package org.jlleitschuh.gradle.ktlint.testdsl + +import net.swiftzer.semver.SemVer +import java.io.File + +fun androidProjectSetup( + agpVersion: String, + kotlinPluginVersion: String, + ktlintVersion: String? = null +): (File) -> Unit = { + val ktLintOverride = ktlintVersion?.let { "ktlint { version = \"$it\" }\n" } ?: "" + val setNamespace = if ((SemVer.parse(agpVersion) >= SemVer(7))) { + " namespace = \"com.example.myapp\"\n" + } else { + "" + } + //language=Groovy + it.resolve("build.gradle").writeText( + """ + |plugins { + | id("com.android.application") + | id("org.jetbrains.kotlin.android") + | id("org.jlleitschuh.gradle.ktlint") + |} + | + |repositories { + | mavenCentral() + |} + |android { + | compileSdk = 33 + |$setNamespace} + |$ktLintOverride + """.trimMargin() + ) + + // before 4.2.0, AGP did not properly publish metadata for id resolution + val oldAgpHack = if ((SemVer.parse(agpVersion) < SemVer(4, 2))) { + """ + | resolutionStrategy { + | eachPlugin { + | when (requested.id.id) { + | "com.android.application" -> useModule("com.android.tools.build:gradle:$agpVersion") + | } + | } + | } + | + """.trimMargin() + } else { + "" + } + val newAgp = if ((SemVer.parse(agpVersion) < SemVer(4, 2))) { + "" + } else { + " id(\"com.android.application\") version \"$agpVersion\"\n " + } + + //language=Groovy + it.resolve("settings.gradle.kts").writeText( + """ + |pluginManagement { + | repositories { + | mavenLocal() + | gradlePluginPortal() + | google() + | mavenCentral() + | } + | + | plugins { + | id("org.jetbrains.kotlin.android") version("$kotlinPluginVersion") + | id("org.jlleitschuh.gradle.ktlint") version("${TestVersions.pluginVersion}") + | $newAgp} + |$oldAgpHack} + | + """.trimMargin() + ) +} diff --git a/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/testdsl/JavaUtil.kt b/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/testdsl/JavaUtil.kt new file mode 100644 index 00000000..d46dda61 --- /dev/null +++ b/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/testdsl/JavaUtil.kt @@ -0,0 +1,10 @@ +package org.jlleitschuh.gradle.ktlint.testdsl + +fun getMajorJavaVersion(): Int { + val specVersion = System.getProperty("java.specification.version") + return if (specVersion.startsWith("1.")) { + specVersion.split(".")[1].toInt() + } else { + return specVersion.toInt() + } +} diff --git a/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/testdsl/TestAnnotations.kt b/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/testdsl/TestAnnotations.kt index e3416888..c649975b 100644 --- a/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/testdsl/TestAnnotations.kt +++ b/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/testdsl/TestAnnotations.kt @@ -1,7 +1,6 @@ package org.jlleitschuh.gradle.ktlint.testdsl import org.gradle.util.GradleVersion -import org.jetbrains.kotlin.utils.addToStdlib.cast import org.jlleitschuh.gradle.ktlint.KtlintBasePlugin import org.junit.jupiter.api.extension.ExtensionContext import org.junit.jupiter.params.provider.Arguments @@ -10,12 +9,15 @@ import java.io.File import java.util.stream.Stream import kotlin.streams.asStream +@Suppress("ConstPropertyName") object TestVersions { const val minSupportedGradleVersion = KtlintBasePlugin.LOWEST_SUPPORTED_GRADLE_VERSION - const val maxSupportedGradleVersion = "8.4" + const val maxSupportedGradleVersion = "8.5" val pluginVersion = File("VERSION_CURRENT.txt").readText().trim() const val minSupportedKotlinPluginVersion = "1.4.32" - const val maxSupportedKotlinPluginVersion = "1.9.10" + const val maxSupportedKotlinPluginVersion = "1.9.21" + const val minAgpVersion = "4.1.0" + const val maxAgpVersion = "8.2.0" } @Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) @@ -42,11 +44,16 @@ open class GradleArgumentsProvider : ArgumentsProvider { .testMethod .get() .annotations - .firstOrNull { it is GradleTestVersions } - ?.let { it.cast() } - ?: context.testClass.get().annotations.first { it is GradleTestVersions }.cast() + .filterIsInstance() + .firstOrNull() + ?: context.testClass.get().annotations.filterIsInstance().first() - val minGradleVersion = GradleVersion.version(versionsAnnotation.minVersion) + val minGradleVersion = if (getMajorJavaVersion() >= 21) { + // Gradle 8.5 is needed to run on Java 21 + GradleVersion.version("8.5") + } else { + GradleVersion.version(versionsAnnotation.minVersion) + } val maxGradleVersion = GradleVersion.version(versionsAnnotation.maxVersion) val additionalGradleVersions = versionsAnnotation .additionalVersions diff --git a/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/testdsl/TestDsl.kt b/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/testdsl/TestDsl.kt index 2594d3ff..0e4533ef 100644 --- a/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/testdsl/TestDsl.kt +++ b/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/testdsl/TestDsl.kt @@ -40,9 +40,9 @@ class TestProject( val settingsGradle get() = projectPath.resolve("settings.gradle") val editorConfig get() = projectPath.resolve(".editorconfig") - fun withCleanSources() { + fun withCleanSources(filePath: String = CLEAN_SOURCES_FILE) { createSourceFile( - CLEAN_SOURCES_FILE, + filePath, """ |val foo = "bar" | diff --git a/settings.gradle.kts b/settings.gradle.kts index ce826a9b..2dd1e85c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -6,6 +6,7 @@ pluginManagement { plugins { id("org.jetbrains.kotlin.jvm") version "1.8.22" id("org.jetbrains.kotlin.js") version "1.8.22" + id("com.android.application") version "4.2.2" } repositories { @@ -13,15 +14,6 @@ pluginManagement { google() maven("https://dl.bintray.com/jetbrains/kotlin-native-dependencies") } - - resolutionStrategy { - eachPlugin { - when (requested.id.id) { - "com.android.application" -> - useModule("com.android.tools.build:gradle:4.1.3") - } - } - } } enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")