diff --git a/build.gradle.kts b/build.gradle.kts index ab6275b60e..a9159a88cd 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,4 @@ -import com.google.devtools.ksp.configureKtlint -import com.google.devtools.ksp.configureKtlintApplyToIdea +import com.google.devtools.ksp.configureKtfmt import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension import org.jetbrains.kotlin.gradle.dsl.KotlinVersion @@ -45,11 +44,10 @@ nexusPublishing { version = rootProject.extra.get("kspVersion") as String -configureKtlintApplyToIdea() subprojects { group = "com.google.devtools.ksp" version = rootProject.extra.get("kspVersion") as String - configureKtlint() + configureKtfmt() repositories { google() maven("https://redirector.kotlinlang.org/maven/bootstrap/") diff --git a/buildSrc/src/main/kotlin/com/google/devtools/ksp/Ktfmt.kt b/buildSrc/src/main/kotlin/com/google/devtools/ksp/Ktfmt.kt new file mode 100644 index 0000000000..bfb685ec57 --- /dev/null +++ b/buildSrc/src/main/kotlin/com/google/devtools/ksp/Ktfmt.kt @@ -0,0 +1,94 @@ +/* + * Copyright 2026 Google LLC + * Copyright 2010-2026 JetBrains s.r.o. and Kotlin Programming Language contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.devtools.ksp + +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.tasks.JavaExec +import org.gradle.api.tasks.PathSensitivity + +private const val KTFMT_VERSION = "0.63" + +/** + * Configures a "ktfmt" task for the project to check formatting and `ktfmtFormat` task + * to fix formatting. Also registers backward-compatible aliases `ktlint` and `ktlintFormat`. + */ +fun Project.configureKtfmt() { + val ktfmtCheck = tasks.register("ktfmt", JavaExec::class.java) { task -> + task.configureCommonKtfmtParams(this@configureKtfmt, dryRun = true) + task.description = "Check Kotlin code style using ktfmt." + task.group = "Verification" + } + + if (false) { // TODO: Remove this guard when all files have been formatted. + afterEvaluate { + project.tasks.named("check").configure { checkTask -> + checkTask.dependsOn(ktfmtCheck) + } + } + } + + val ktfmtFormat = tasks.register("ktfmtFormat", JavaExec::class.java) { task -> + task.configureCommonKtfmtParams(this@configureKtfmt, dryRun = false) + task.description = "Fix Kotlin code style deviations using ktfmt." + task.group = "formatting" + } +} + +private fun JavaExec.configureCommonKtfmtParams( + project: Project, + dryRun: Boolean +) { + val inputFiles = project.fileTree(project.projectDir).also { + it.include("**/*.kt") + it.include("**/*.kts") + it.exclude("**/testData/**") + it.exclude("**/build/**") + it.exclude("dist/**") + it.exclude("**/.*/**") + it.exclude("**/resources/**") + } + val outputFile = project.layout.buildDirectory.file("reports/ktfmt/ktfmtCheckSuccess.txt") + .map { it.asFile.toRelativeString(project.layout.projectDirectory.asFile) } + .get() + inputs.files(inputFiles).withPropertyName("ktfmtInputFiles").withPathSensitivity(PathSensitivity.RELATIVE) + classpath = project.getKtfmtConfiguration() + mainClass.set("com.facebook.ktfmt.cli.Main") + + if (dryRun) { + outputs.file(outputFile).withPropertyName("ktfmtOutputFile") + outputs.cacheIf { true } + doLast { + val file = project.file(outputFile) + file.parentFile.mkdirs() + file.writeText("SUCCESS") + } + } else { + outputs.upToDateWhen { false } + } + + args = listOf("--kotlinlang-style") + + (if (dryRun) listOf("--dry-run", "--set-exit-if-changed") else emptyList()) + + inputFiles.files.map { it.toRelativeString(project.projectDir) } +} + +private fun Project.getKtfmtConfiguration(): Configuration { + return configurations.findByName("ktfmt") ?: configurations.create("ktfmt") { + val dependency = dependencies.create("com.facebook:ktfmt:$KTFMT_VERSION:with-dependencies") + it.dependencies.add(dependency) + } +} diff --git a/buildSrc/src/main/kotlin/com/google/devtools/ksp/Ktlint.kt b/buildSrc/src/main/kotlin/com/google/devtools/ksp/Ktlint.kt deleted file mode 100644 index a3c9e09d39..0000000000 --- a/buildSrc/src/main/kotlin/com/google/devtools/ksp/Ktlint.kt +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2020 Google LLC - * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.google.devtools.ksp - -import org.gradle.api.Project -import org.gradle.api.artifacts.Configuration -import org.gradle.api.tasks.JavaExec -import org.gradle.api.tasks.PathSensitivity - -// This file is mostly ported from AndroidX with minor modifications. -// https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:buildSrc/src/main/kotlin/androidx/build/Ktlint.kt - -/** - * Adds a ktlintApplyToIdea task that updates the local .idea files to match the ktlint style. - */ -fun Project.configureKtlintApplyToIdea() { - if (this.rootProject !== this) { - throw IllegalArgumentException("Can only use root project for applyToIdea task") - } - tasks.register("ktlintApplyToIdea", JavaExec::class.java) { task -> - task.description = "Apply ktlint style to idea" - task.group = "Tooling" - task.classpath = getKtlintConfiguration() - task.mainClass.set("com.pinterest.ktlint.Main") - task.args = listOf( - "applyToIDEAProject", - "-y" - ) - } -} - -/** - * Configures a "ktlint" task for the project to check formatting and `ktlintFormat` task - * to fix formatting. - */ -fun Project.configureKtlint() { - val lintProvider = tasks.register("ktlint", JavaExec::class.java) { task -> - task.configureCommonKtlintParams(this@configureKtlint) - task.description = "Check Kotlin code style." - task.group = "Verification" - } - - afterEvaluate { - // check task is not available yet, which is why we use afterEvaluate - project.tasks.named("check").configure { checkTask -> - checkTask.dependsOn(lintProvider) - } - } - - tasks.register("ktlintFormat", JavaExec::class.java) { task -> - task.configureCommonKtlintParams(this@configureKtlint) - task.description = "Fix Kotlin code style deviations." - task.group = "formatting" - task.args = listOf("-F") + task.args - } -} - -/** - * Configures common ktlint parameters for ktlint tasks - */ -private fun JavaExec.configureCommonKtlintParams( - project: Project -) { - val ktlintInputFiles = project.fileTree(project.projectDir).also { - it.include("**/*.kt") - it.include("**/*.kts") - it.exclude("**/testData/**") - it.exclude("**/build/**") - it.exclude("dist/**") - it.exclude("**/.*/**") - it.exclude("**/resources/**") - } - val outputFile = project.layout.buildDirectory.file("reports/ktlint/ktlint-checkstyle-report.xml") - .map{ it.asFile.toRelativeString(project.layout.projectDirectory.asFile) } - .get() - inputs.files(ktlintInputFiles).withPropertyName("ktlintInputFiles").withPathSensitivity(PathSensitivity.RELATIVE) - classpath = project.getKtlintConfiguration() - mainClass.set("com.pinterest.ktlint.Main") - outputs.file(outputFile).withPropertyName("ktlintOutputFile") - outputs.cacheIf { true } - args = listOf( - "--reporter=plain", - "--reporter=checkstyle,output=$outputFile", - ) + ktlintInputFiles.files.map { it.toRelativeString(project.projectDir) } -} - -private fun Project.getKtlintConfiguration(): Configuration { - return configurations.findByName("ktlint") ?: configurations.create("ktlint") { - val dependency = dependencies.create("com.pinterest:ktlint:0.40.0") - it.dependencies.add(dependency) - } -}