refactor: use worker API for KtfmtFormatTask

This commit is contained in:
Harsh Shandilya 2023-03-04 02:01:15 +05:30
parent 3a694c7255
commit 3e56fa2e12
No known key found for this signature in database
4 changed files with 104 additions and 52 deletions

View File

@ -2,6 +2,7 @@ package app.passwordstore.gradle
import app.passwordstore.gradle.ktfmt.KtfmtCheckTask import app.passwordstore.gradle.ktfmt.KtfmtCheckTask
import app.passwordstore.gradle.ktfmt.KtfmtFormatTask import app.passwordstore.gradle.ktfmt.KtfmtFormatTask
import java.util.concurrent.Callable
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.kotlin.dsl.register import org.gradle.kotlin.dsl.register
@ -9,23 +10,14 @@ import org.gradle.kotlin.dsl.register
class KtfmtPlugin : Plugin<Project> { class KtfmtPlugin : Plugin<Project> {
override fun apply(target: Project) { override fun apply(target: Project) {
target.tasks.register<KtfmtFormatTask>("ktfmtFormat") { val input = Callable {
source = target.layout.projectDirectory.asFileTree.filter { file ->
project.layout.projectDirectory.asFileTree file.extension == "kt" || file.extension == "kts" && !file.canonicalPath.contains("build")
.filter { file ->
file.extension == "kt" ||
file.extension == "kts" && !file.canonicalPath.contains("build")
} }
.asFileTree
} }
target.tasks.register<KtfmtFormatTask>("ktfmtFormat") { source(input) }
target.tasks.register<KtfmtCheckTask>("ktfmtCheck") { target.tasks.register<KtfmtCheckTask>("ktfmtCheck") {
source = source(input)
project.layout.projectDirectory.asFileTree
.filter { file ->
file.extension == "kt" ||
file.extension == "kts" && !file.canonicalPath.contains("build")
}
.asFileTree
projectDirectory.set(target.layout.projectDirectory) projectDirectory.set(target.layout.projectDirectory)
} }
} }

View File

@ -1,52 +1,53 @@
package app.passwordstore.gradle.ktfmt package app.passwordstore.gradle.ktfmt
import com.facebook.ktfmt.format.Formatter import javax.inject.Inject
import com.facebook.ktfmt.format.FormattingOptions import org.gradle.api.GradleException
import java.io.File import org.gradle.api.file.ProjectLayout
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.file.FileCollection
import org.gradle.api.tasks.IgnoreEmptyDirectories
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.PathSensitive
import org.gradle.api.tasks.PathSensitivity
import org.gradle.api.tasks.SourceTask import org.gradle.api.tasks.SourceTask
import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.TaskAction
import org.gradle.internal.exceptions.MultiCauseException
import org.gradle.workers.WorkerExecutor
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
@OptIn(ExperimentalCoroutinesApi::class) abstract class KtfmtFormatTask
abstract class KtfmtFormatTask : SourceTask() { @Inject
constructor(
private val workerExecutor: WorkerExecutor,
private val projectLayout: ProjectLayout,
) : SourceTask() {
@get:PathSensitive(PathSensitivity.RELATIVE) init {
@get:InputFiles outputs.upToDateWhen { false }
@get:IgnoreEmptyDirectories }
protected val inputFiles: FileCollection
get() = super.getSource()
@TaskAction @TaskAction
fun execute() { fun execute() {
runBlocking(Dispatchers.IO.limitedParallelism(PARALLEL_TASK_LIMIT)) { val result =
coroutineScope { inputFiles.map { async { formatFile(it) } }.awaitAll() } with(workerExecutor.noIsolation()) {
submit(KtfmtWorkerAction::class.java) {
name.set("foofoo")
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 fun formatFile(input: File) { private inline fun <reified T : Throwable> Throwable.workErrorCauses(): List<Throwable> {
val originCode = input.readText() return when (this) {
val formattedCode = is MultiCauseException -> this.causes.map { it.cause }
Formatter.format( else -> listOf(this.cause)
FormattingOptions(
style = FormattingOptions.Style.GOOGLE,
maxWidth = 100,
continuationIndent = 2,
),
originCode
)
if (originCode != formattedCode) {
input.writeText(formattedCode)
} }
.filter {
// class instance comparison doesn't work due to different classloaders
it?.javaClass?.canonicalName == T::class.java.canonicalName
}
.filterNotNull()
} }
companion object { companion object {

View File

@ -0,0 +1,46 @@
package app.passwordstore.gradle.ktfmt
import com.facebook.ktfmt.format.Formatter
import com.facebook.ktfmt.format.FormattingOptions
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(
FormattingOptions(
style = FormattingOptions.Style.GOOGLE,
maxWidth = 100,
continuationIndent = 2,
),
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)
}
}
}

View File

@ -0,0 +1,13 @@
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
val output: RegularFileProperty
}