aboutsummaryrefslogtreecommitdiff
path: root/build-logic/src
diff options
context:
space:
mode:
authorHarsh Shandilya <me@msfjarvis.dev>2024-08-14 00:18:51 +0530
committerHarsh Shandilya <me@msfjarvis.dev>2024-08-14 00:18:51 +0530
commitb699b4db71d08b574cc31daf4ee8c9b849ebaa11 (patch)
tree50dd1fddb1c2c270a28d5819af2bbe570fcbb289 /build-logic/src
parent3af68b45c4cfd815b9975108fa09cb3872fbab8b (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')
-rw-r--r--build-logic/src/main/kotlin/app/passwordstore/gradle/KtfmtPlugin.kt37
-rw-r--r--build-logic/src/main/kotlin/app/passwordstore/gradle/SpotlessPlugin.kt44
-rw-r--r--build-logic/src/main/kotlin/app/passwordstore/gradle/ktfmt/KtfmtCheckTask.kt63
-rw-r--r--build-logic/src/main/kotlin/app/passwordstore/gradle/ktfmt/KtfmtDiffEntry.kt8
-rw-r--r--build-logic/src/main/kotlin/app/passwordstore/gradle/ktfmt/KtfmtDiffer.kt28
-rw-r--r--build-logic/src/main/kotlin/app/passwordstore/gradle/ktfmt/KtfmtFormatTask.kt46
-rw-r--r--build-logic/src/main/kotlin/app/passwordstore/gradle/ktfmt/KtfmtWorkerAction.kt38
-rw-r--r--build-logic/src/main/kotlin/app/passwordstore/gradle/ktfmt/KtfmtWorkerParameters.kt12
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
-}