diff options
author | Harsh Shandilya <me@msfjarvis.dev> | 2024-08-14 00:18:51 +0530 |
---|---|---|
committer | Harsh Shandilya <me@msfjarvis.dev> | 2024-08-14 00:18:51 +0530 |
commit | b699b4db71d08b574cc31daf4ee8c9b849ebaa11 (patch) | |
tree | 50dd1fddb1c2c270a28d5819af2bbe570fcbb289 /build-logic/src/main | |
parent | 3af68b45c4cfd815b9975108fa09cb3872fbab8b (diff) |
feat(build): replace homebrew ktfmt formatter with Spotless
Spotless has fixed their Gradle Configuration Cache woes in the past
couple months which gets rid of my primary complaint.
Diffstat (limited to 'build-logic/src/main')
8 files changed, 44 insertions, 232 deletions
diff --git a/build-logic/src/main/kotlin/app/passwordstore/gradle/KtfmtPlugin.kt b/build-logic/src/main/kotlin/app/passwordstore/gradle/KtfmtPlugin.kt deleted file mode 100644 index 7aac48b5..00000000 --- a/build-logic/src/main/kotlin/app/passwordstore/gradle/KtfmtPlugin.kt +++ /dev/null @@ -1,37 +0,0 @@ -package app.passwordstore.gradle - -import app.passwordstore.gradle.ktfmt.KtfmtCheckTask -import app.passwordstore.gradle.ktfmt.KtfmtFormatTask -import com.facebook.ktfmt.format.FormattingOptions -import java.util.concurrent.Callable -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.kotlin.dsl.register - -class KtfmtPlugin : Plugin<Project> { - - override fun apply(target: Project) { - val input = Callable { - target.layout.projectDirectory.asFileTree.filter { file -> - file.extension == "kt" || file.extension == "kts" && !file.canonicalPath.contains("build/") - } - } - target.tasks.register<KtfmtFormatTask>("ktfmtFormat") { source(input) } - target.tasks.register<KtfmtCheckTask>("ktfmtCheck") { - source(input) - projectDirectory.set(target.layout.projectDirectory) - } - } - - companion object { - val DEFAULT_FORMATTING_OPTIONS = - FormattingOptions( - maxWidth = FormattingOptions.DEFAULT_MAX_WIDTH, - blockIndent = 2, - continuationIndent = 2, - removeUnusedImports = true, - debuggingPrintOpsAfterFormatting = false, - manageTrailingCommas = true, - ) - } -} diff --git a/build-logic/src/main/kotlin/app/passwordstore/gradle/SpotlessPlugin.kt b/build-logic/src/main/kotlin/app/passwordstore/gradle/SpotlessPlugin.kt new file mode 100644 index 00000000..6f33f922 --- /dev/null +++ b/build-logic/src/main/kotlin/app/passwordstore/gradle/SpotlessPlugin.kt @@ -0,0 +1,44 @@ +/* + * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved. + * SPDX-License-Identifier: GPL-3.0-only + */ + +package app.passwordstore.gradle + +import com.diffplug.gradle.spotless.SpotlessExtension +import com.diffplug.gradle.spotless.SpotlessPlugin +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.apply +import org.gradle.kotlin.dsl.getByType + +@Suppress("Unused") +class SpotlessPlugin : Plugin<Project> { + + override fun apply(project: Project) { + project.pluginManager.apply(SpotlessPlugin::class) + project.extensions.getByType<SpotlessExtension>().run { + kotlin { + ktfmt(KTFMT_VERSION).googleStyle() + target("**/*.kt") + targetExclude("**/build/") + } + kotlinGradle { + ktfmt(KTFMT_VERSION).googleStyle() + target("**/*.kts") + targetExclude("**/build/") + } + format("xml") { + target("**/*.xml") + targetExclude("**/build/", ".idea/") + trimTrailingWhitespace() + indentWithSpaces() + endWithNewline() + } + } + } + + private companion object { + private const val KTFMT_VERSION = "0.51" + } +} diff --git a/build-logic/src/main/kotlin/app/passwordstore/gradle/ktfmt/KtfmtCheckTask.kt b/build-logic/src/main/kotlin/app/passwordstore/gradle/ktfmt/KtfmtCheckTask.kt deleted file mode 100644 index 58ef432f..00000000 --- a/build-logic/src/main/kotlin/app/passwordstore/gradle/ktfmt/KtfmtCheckTask.kt +++ /dev/null @@ -1,63 +0,0 @@ -package app.passwordstore.gradle.ktfmt - -import app.passwordstore.gradle.KtfmtPlugin -import com.facebook.ktfmt.format.Formatter -import java.io.File -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.async -import kotlinx.coroutines.awaitAll -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.runBlocking -import org.gradle.api.GradleException -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.file.FileCollection -import org.gradle.api.tasks.IgnoreEmptyDirectories -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.TaskAction - -@OptIn(ExperimentalCoroutinesApi::class) -abstract class KtfmtCheckTask : SourceTask() { - - @get:PathSensitive(PathSensitivity.RELATIVE) - @get:InputFiles - @get:IgnoreEmptyDirectories - protected val inputFiles: FileCollection - get() = super.getSource() - - @get:Internal abstract val projectDirectory: DirectoryProperty - - @TaskAction - fun execute() { - runBlocking(Dispatchers.IO.limitedParallelism(PARALLEL_TASK_LIMIT)) { - coroutineScope { - val results = inputFiles.map { async { checkFile(it) } }.awaitAll() - if (results.any { (notFormatted, _) -> notFormatted }) { - val prettyDiff = - results - .map { (_, diffs) -> diffs } - .flatten() - .joinToString(separator = "\n") { diff -> diff.toString() } - throw GradleException("[ktfmt] Found unformatted files\n${prettyDiff}") - } - } - } - } - - private fun checkFile(input: File): Pair<Boolean, List<KtfmtDiffEntry>> { - val originCode = input.readText() - val formattedCode = Formatter.format(KtfmtPlugin.DEFAULT_FORMATTING_OPTIONS, originCode) - val pathNormalizer = { file: File -> file.toRelativeString(projectDirectory.asFile.get()) } - return (originCode != formattedCode) to - KtfmtDiffer.computeDiff(input, formattedCode, pathNormalizer) - } - - companion object { - - private const val PARALLEL_TASK_LIMIT = 4 - } -} diff --git a/build-logic/src/main/kotlin/app/passwordstore/gradle/ktfmt/KtfmtDiffEntry.kt b/build-logic/src/main/kotlin/app/passwordstore/gradle/ktfmt/KtfmtDiffEntry.kt deleted file mode 100644 index ca01b5e9..00000000 --- a/build-logic/src/main/kotlin/app/passwordstore/gradle/ktfmt/KtfmtDiffEntry.kt +++ /dev/null @@ -1,8 +0,0 @@ -package app.passwordstore.gradle.ktfmt - -data class KtfmtDiffEntry(val input: String, val lineNumber: Int, val message: String) { - - override fun toString(): String { - return "$input:$lineNumber - $message" - } -} diff --git a/build-logic/src/main/kotlin/app/passwordstore/gradle/ktfmt/KtfmtDiffer.kt b/build-logic/src/main/kotlin/app/passwordstore/gradle/ktfmt/KtfmtDiffer.kt deleted file mode 100644 index f1332923..00000000 --- a/build-logic/src/main/kotlin/app/passwordstore/gradle/ktfmt/KtfmtDiffer.kt +++ /dev/null @@ -1,28 +0,0 @@ -package app.passwordstore.gradle.ktfmt - -import com.github.difflib.DiffUtils -import com.github.difflib.patch.ChangeDelta -import com.github.difflib.patch.DeleteDelta -import com.github.difflib.patch.InsertDelta -import java.io.File - -object KtfmtDiffer { - fun computeDiff( - inputFile: File, - formattedCode: String, - pathNormalizer: (File) -> String, - ): List<KtfmtDiffEntry> { - val originCode = inputFile.readText() - return DiffUtils.diff(originCode, formattedCode, null).deltas.map { - val line = it.source.position + 1 - val message: String = - when (it) { - is ChangeDelta -> "Line changed: ${it.source.lines.first()}" - is DeleteDelta -> "Line deleted" - is InsertDelta -> "Line added" - else -> "" - } - KtfmtDiffEntry(pathNormalizer(inputFile), line, message) - } - } -} diff --git a/build-logic/src/main/kotlin/app/passwordstore/gradle/ktfmt/KtfmtFormatTask.kt b/build-logic/src/main/kotlin/app/passwordstore/gradle/ktfmt/KtfmtFormatTask.kt deleted file mode 100644 index c76b5c89..00000000 --- a/build-logic/src/main/kotlin/app/passwordstore/gradle/ktfmt/KtfmtFormatTask.kt +++ /dev/null @@ -1,46 +0,0 @@ -package app.passwordstore.gradle.ktfmt - -import javax.inject.Inject -import org.gradle.api.GradleException -import org.gradle.api.file.ProjectLayout -import org.gradle.api.tasks.SourceTask -import org.gradle.api.tasks.TaskAction -import org.gradle.internal.exceptions.MultiCauseException -import org.gradle.workers.WorkerExecutor -import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty - -abstract class KtfmtFormatTask -@Inject -constructor(private val workerExecutor: WorkerExecutor, private val projectLayout: ProjectLayout) : - SourceTask() { - - @TaskAction - fun execute() { - val result = - with(workerExecutor.noIsolation()) { - submit(KtfmtWorkerAction::class.java) { - name.set("ktfmt-worker") - files.from(source) - projectDirectory.set(projectLayout.projectDirectory.asFile) - } - runCatching { await() } - } - - result.exceptionOrNull()?.workErrorCauses<Exception>()?.ifNotEmpty { - forEach { logger.error(it.message, it.cause) } - throw GradleException("error formatting sources for $name") - } - } - - private inline fun <reified T : Throwable> Throwable.workErrorCauses(): List<Throwable> { - return when (this) { - is MultiCauseException -> this.causes.map { it.cause } - else -> listOf(this.cause) - } - .filter { - // class instance comparison doesn't work due to different classloaders - it?.javaClass?.canonicalName == T::class.java.canonicalName - } - .filterNotNull() - } -} diff --git a/build-logic/src/main/kotlin/app/passwordstore/gradle/ktfmt/KtfmtWorkerAction.kt b/build-logic/src/main/kotlin/app/passwordstore/gradle/ktfmt/KtfmtWorkerAction.kt deleted file mode 100644 index c955adbe..00000000 --- a/build-logic/src/main/kotlin/app/passwordstore/gradle/ktfmt/KtfmtWorkerAction.kt +++ /dev/null @@ -1,38 +0,0 @@ -package app.passwordstore.gradle.ktfmt - -import app.passwordstore.gradle.KtfmtPlugin -import com.facebook.ktfmt.format.Formatter -import java.io.File -import org.gradle.api.logging.LogLevel -import org.gradle.api.logging.Logger -import org.gradle.api.logging.Logging -import org.gradle.internal.logging.slf4j.DefaultContextAwareTaskLogger -import org.gradle.workers.WorkAction - -abstract class KtfmtWorkerAction : WorkAction<KtfmtWorkerParameters> { - private val logger: Logger = - DefaultContextAwareTaskLogger(Logging.getLogger(KtfmtFormatTask::class.java)) - private val files: List<File> = parameters.files.toList() - private val projectDirectory: File = parameters.projectDirectory.asFile.get() - private val name: String = parameters.name.get() - - override fun execute() { - try { - files.forEach { file -> - val sourceText = file.readText() - val relativePath = file.toRelativeString(projectDirectory) - - logger.log(LogLevel.DEBUG, "$name checking format: $relativePath") - - val formattedText = Formatter.format(KtfmtPlugin.DEFAULT_FORMATTING_OPTIONS, sourceText) - - if (!formattedText.contentEquals(sourceText)) { - logger.log(LogLevel.QUIET, "${file.toRelativeString(projectDirectory)}: Format fixed") - file.writeText(formattedText) - } - } - } catch (t: Throwable) { - throw Exception("format worker execution error", t) - } - } -} diff --git a/build-logic/src/main/kotlin/app/passwordstore/gradle/ktfmt/KtfmtWorkerParameters.kt b/build-logic/src/main/kotlin/app/passwordstore/gradle/ktfmt/KtfmtWorkerParameters.kt deleted file mode 100644 index 1c550de8..00000000 --- a/build-logic/src/main/kotlin/app/passwordstore/gradle/ktfmt/KtfmtWorkerParameters.kt +++ /dev/null @@ -1,12 +0,0 @@ -package app.passwordstore.gradle.ktfmt - -import org.gradle.api.file.ConfigurableFileCollection -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.provider.Property -import org.gradle.workers.WorkParameters - -interface KtfmtWorkerParameters : WorkParameters { - val name: Property<String> - val files: ConfigurableFileCollection - val projectDirectory: RegularFileProperty -} |