Skip to content

Commit

Permalink
cortinico#181 - move ktfmt to use Worker API for classloader isolation (
Browse files Browse the repository at this point in the history
cortinico#182)

Co-authored-by: Nicola Corti <[email protected]>
  • Loading branch information
cloudshiftchris and cortinico authored Aug 4, 2023
1 parent 2fc32fd commit e811d0b
Show file tree
Hide file tree
Showing 24 changed files with 590 additions and 426 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
/local.properties
.DS_Store
/build
.idea/
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ Please add your entries according to this format.

## Unreleased

- Add support for Gradle Worker API and Classloader Isolation (#182)

## Version 0.13.0 *(2023-07-15)*

- Handle implicit dependency error message for KMP project (#171)
Expand Down
3 changes: 0 additions & 3 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
[versions]
agp = "8.1.0"
binaryCompatibilityValidator = "0.13.2"
coroutines = "1.7.3"
detekt = "1.23.1"
diffUtils = "4.12"
junit = "5.10.0"
Expand All @@ -28,8 +27,6 @@ junit-bom = { module = "org.junit:junit-bom", version.ref = "junit" }
jupiter = { module = "org.junit.jupiter:junit-jupiter" }
jupiter-platform-launcher = { module = "org.junit.platform:junit-platform-launcher" }
kotlin-compiler-embeddable = { module = "org.jetbrains.kotlin:kotlin-compiler-embeddable", version.ref = "kotlin" }
coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" }
truth = { module = "com.google.truth:truth", version.ref = "truth" }
diffUtils = { module = "io.github.java-diff-utils:java-diff-utils", version.ref = "diffUtils" }
ktfmt = { module = "com.facebook:ktfmt", version.ref = "ktfmt" }
23 changes: 10 additions & 13 deletions plugin-build/plugin/api/plugin.api
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
public abstract class com/ncorti/ktfmt/gradle/KtfmtExtension {
public fun <init> (Lorg/gradle/api/Project;)V
public fun <init> ()V
public final fun dropboxStyle ()V
public final fun getBlockIndent ()Lorg/gradle/api/provider/Property;
public final fun getContinuationIndent ()Lorg/gradle/api/provider/Property;
public final fun getDebuggingPrintOpsAfterFormatting ()Lorg/gradle/api/provider/Property;
public final fun getMaxWidth ()Lorg/gradle/api/provider/Property;
public final fun getRemoveUnusedImports ()Lorg/gradle/api/provider/Property;
public abstract fun getBlockIndent ()Lorg/gradle/api/provider/Property;
public abstract fun getContinuationIndent ()Lorg/gradle/api/provider/Property;
public abstract fun getDebuggingPrintOpsAfterFormatting ()Lorg/gradle/api/provider/Property;
public abstract fun getMaxWidth ()Lorg/gradle/api/provider/Property;
public abstract fun getRemoveUnusedImports ()Lorg/gradle/api/provider/Property;
public final fun googleStyle ()V
public final fun kotlinLangStyle ()V
}
Expand All @@ -21,19 +21,16 @@ public final class com/ncorti/ktfmt/gradle/KtfmtPlugin$Companion {
}

public abstract class com/ncorti/ktfmt/gradle/tasks/KtfmtBaseTask : org/gradle/api/tasks/SourceTask {
public fun <init> ()V
protected abstract fun execute (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun getIncludeOnly ()Lorg/gradle/api/provider/Property;
protected abstract fun execute (Lorg/gradle/workers/WorkQueue;)V
public abstract fun getIncludeOnly ()Lorg/gradle/api/provider/Property;
}

public abstract class com/ncorti/ktfmt/gradle/tasks/KtfmtCheckTask : com/ncorti/ktfmt/gradle/tasks/KtfmtBaseTask {
public fun <init> ()V
protected fun execute (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
protected fun execute (Lorg/gradle/workers/WorkQueue;)V
}

public abstract class com/ncorti/ktfmt/gradle/tasks/KtfmtFormatTask : com/ncorti/ktfmt/gradle/tasks/KtfmtBaseTask {
public fun <init> ()V
protected fun execute (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
protected fun execute (Lorg/gradle/workers/WorkQueue;)V
protected final fun getOutputFiles ()Lorg/gradle/api/file/FileCollection;
}

20 changes: 17 additions & 3 deletions plugin-build/plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -48,22 +48,22 @@ tasks.withType<KotlinCompile>().configureEach {
}

dependencies {
implementation(libs.coroutines.core)
implementation(libs.ktfmt)
compileOnly(libs.ktfmt)
implementation(libs.diffUtils)

compileOnly(gradleApi())
compileOnly(kotlin("gradle-plugin"))
compileOnly(libs.agp)

testImplementation(libs.coroutines.test)
testImplementation(kotlin("gradle-plugin"))
testImplementation(libs.agp)

testImplementation(platform(libs.junit.bom))
testImplementation(libs.jupiter)
testImplementation(libs.truth)

testImplementation(libs.ktfmt)

testRuntimeOnly(
files(
serviceOf<ModuleRegistry>().getModule("gradle-tooling-api-builders")
Expand Down Expand Up @@ -102,3 +102,17 @@ signing {
tasks.withType<Test> {
useJUnitPlatform()
}

val persistKtmftVersion by tasks.registering {
inputs.property("ktfmtVersion", libs.ktfmt)
outputs.files(layout.buildDirectory.file("ktfmt-version.txt"))
doLast {
outputs.files.singleFile.writeText(inputs.properties["ktfmtVersion"].toString())
}
}

tasks.named<ProcessResources>("processResources") {
from(persistKtmftVersion) {
into("com/ncorti/ktfmt/gradle")
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
package com.ncorti.ktfmt.gradle

import com.facebook.ktfmt.format.FormattingOptions
import com.facebook.ktfmt.format.FormattingOptions.Companion.DEFAULT_MAX_WIDTH
import com.facebook.ktfmt.format.FormattingOptions.Style as KtfmtStyle
import java.io.Serializable

internal data class FormattingOptionsBean(
Expand All @@ -11,7 +8,7 @@ internal data class FormattingOptionsBean(
val style: Style = Style.FACEBOOK,

/** ktfmt breaks lines longer than maxWidth. */
val maxWidth: Int = DEFAULT_MAX_WIDTH,
val maxWidth: Int = defaultMaxWidth,

/**
* blockIndent is the size of the indent used when a new block is opened, in spaces.
Expand Down Expand Up @@ -46,30 +43,14 @@ internal data class FormattingOptionsBean(
*/
val debuggingPrintOpsAfterFormatting: Boolean = false
) : Serializable {
fun toFormattingOptions(): FormattingOptions =
FormattingOptions(
style = style.toKtfmtStyle(),
maxWidth = maxWidth,
blockIndent = blockIndent,
continuationIndent = continuationIndent,
removeUnusedImports = removeUnusedImports,
debuggingPrintOpsAfterFormatting = debuggingPrintOpsAfterFormatting
)

enum class Style {
FACEBOOK,
DROPBOX,
GOOGLE;

fun toKtfmtStyle(): KtfmtStyle =
when (this) {
FACEBOOK -> KtfmtStyle.FACEBOOK
DROPBOX -> KtfmtStyle.DROPBOX
GOOGLE -> KtfmtStyle.GOOGLE
}
GOOGLE
}

companion object {
private const val serialVersionUID: Long = 1L
private const val defaultMaxWidth = 100
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,23 @@ package com.ncorti.ktfmt.gradle
import com.ncorti.ktfmt.gradle.FormattingOptionsBean.Style.DROPBOX
import com.ncorti.ktfmt.gradle.FormattingOptionsBean.Style.FACEBOOK
import com.ncorti.ktfmt.gradle.FormattingOptionsBean.Style.GOOGLE
import javax.inject.Inject
import org.gradle.api.Project
import org.gradle.api.provider.Property

/** Gradle Extension to help you configure ktfmt-gradle */
@Suppress("UnnecessaryAbstractClass")
abstract class KtfmtExtension @Inject constructor(project: Project) {

private val objects = project.objects
abstract class KtfmtExtension {
init {
maxWidth.convention(DEFAULT_MAX_WIDTH)
blockIndent.convention(DEFAULT_BLOCK_INDENT)
continuationIndent.convention(DEFAULT_CONTINUATION_INDENT)
removeUnusedImports.convention(DEFAULT_REMOVE_UNUSED_IMPORTS)
debuggingPrintOpsAfterFormatting.convention(DEFAULT_DEBUGGING_PRINT_OPTS)
}

internal var ktfmtStyle = FACEBOOK

/** ktfmt breaks lines longer than maxWidth. Default 100. */
val maxWidth: Property<Int> = objects.property(Int::class.java).convention(DEFAULT_MAX_WIDTH)
abstract val maxWidth: Property<Int>

/**
* blockIndent is the size of the indent used when a new block is opened, in spaces.
Expand All @@ -28,8 +31,7 @@ abstract class KtfmtExtension @Inject constructor(project: Project) {
* }
* ```
*/
val blockIndent: Property<Int> =
objects.property(Int::class.java).convention(DEFAULT_BLOCK_INDENT)
abstract val blockIndent: Property<Int>

/**
* continuationIndent is the size of the indent used when a line is broken because it's too
Expand All @@ -41,19 +43,16 @@ abstract class KtfmtExtension @Inject constructor(project: Project) {
* 1)
* ```
*/
val continuationIndent: Property<Int> =
objects.property(Int::class.java).convention(DEFAULT_CONTINUATION_INDENT)
abstract val continuationIndent: Property<Int>

/** Whether ktfmt should remove imports that are not used. */
val removeUnusedImports: Property<Boolean> =
objects.property(Boolean::class.java).convention(DEFAULT_REMOVE_UNUSED_IMPORTS)
abstract val removeUnusedImports: Property<Boolean>

/**
* Print the Ops generated by KotlinInputAstVisitor to help reason about formatting (i.e.,
* newline) decisions
*/
val debuggingPrintOpsAfterFormatting: Property<Boolean> =
objects.property(Boolean::class.java).convention(DEFAULT_DEBUGGING_PRINT_OPTS)
abstract val debuggingPrintOpsAfterFormatting: Property<Boolean>

/** Enables --dropbox-style (equivalent to set blockIndent to 4 and continuationIndent to 4). */
@Suppress("MagicNumber")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import com.ncorti.ktfmt.gradle.KtfmtPluginUtils.EXTENSION_NAME
import com.ncorti.ktfmt.gradle.KtfmtPluginUtils.TASK_NAME_CHECK
import com.ncorti.ktfmt.gradle.KtfmtPluginUtils.TASK_NAME_FORMAT
import com.ncorti.ktfmt.gradle.KtfmtPluginUtils.createTasksForSourceSet
import com.ncorti.ktfmt.gradle.tasks.KtfmtBaseTask
import com.ncorti.ktfmt.gradle.util.i
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.attributes.Usage
import org.gradle.api.tasks.TaskProvider
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
Expand All @@ -24,26 +26,55 @@ abstract class KtfmtPlugin : Plugin<Project> {
private lateinit var topLevelFormat: TaskProvider<Task>
private lateinit var topLevelCheck: TaskProvider<Task>

override fun apply(project: Project) {
ktfmtExtension =
project.extensions.create(EXTENSION_NAME, KtfmtExtension::class.java, project)
override fun apply(project: Project) =
project.run {
ktfmtExtension = project.extensions.create(EXTENSION_NAME, KtfmtExtension::class.java)

topLevelFormat = createTopLevelFormatTask(project)
topLevelCheck = createTopLevelCheckTask(project)
// setup to pull in ktfmt separately to run on an isolated classloader
val ktFmt =
configurations.create("ktfmt").apply {
attributes.apply {
attribute(
Usage.USAGE_ATTRIBUTE,
project.objects.named(Usage::class.java, Usage.JAVA_RUNTIME)
)
}
isVisible = false
isCanBeConsumed = false
}

project.plugins.withId("kotlin") { applyKtfmt(project) }
project.plugins.withId("kotlin-android") {
if (project.plugins.hasPlugin("org.jetbrains.kotlin.multiplatform")) {
project.logger.i("Skipping Android task creation, as KMP is applied")
} else {
applyKtfmtToAndroidProject(project, ktfmtExtension, topLevelFormat, topLevelCheck)
val resourceUri =
KtfmtPlugin::class.java.getResource("ktfmt-version.txt")
?: error("Missing ktfmt version")
val ktfmtVersion = resources.text.fromUri(resourceUri).asString()

project.dependencies.add(ktFmt.name, ktfmtVersion)

project.tasks.withType(KtfmtBaseTask::class.java).configureEach {
it.ktfmtClasspath.from(ktFmt)
}

topLevelFormat = createTopLevelFormatTask(project)
topLevelCheck = createTopLevelCheckTask(project)

project.plugins.withId("kotlin") { applyKtfmt(project) }
project.plugins.withId("kotlin-android") {
if (project.plugins.hasPlugin("org.jetbrains.kotlin.multiplatform")) {
project.logger.i("Skipping Android task creation, as KMP is applied")
} else {
applyKtfmtToAndroidProject(
project,
ktfmtExtension,
topLevelFormat,
topLevelCheck
)
}
}
project.plugins.withId("org.jetbrains.kotlin.js") { applyKtfmt(project) }
project.plugins.withId("org.jetbrains.kotlin.multiplatform") {
applyKtfmtToMultiplatformProject(project)
}
}
project.plugins.withId("org.jetbrains.kotlin.js") { applyKtfmt(project) }
project.plugins.withId("org.jetbrains.kotlin.multiplatform") {
applyKtfmtToMultiplatformProject(project)
}
}

private fun applyKtfmt(project: Project) {
val extension = project.extensions.getByType(KotlinProjectExtension::class.java)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.ncorti.ktfmt.gradle.tasks

import java.io.File

internal object IncludedFilesParser {
fun parse(value: String, rootDir: File): Set<File> {
if (value.isBlank()) return emptySet()
return value
.split(',', ':')
.map {
val relativePath = it.removePrefix("/").removePrefix("\\")
rootDir.resolve(relativePath.replace("\\", "/")).canonicalFile
}
.toSet()
}
}
Loading

0 comments on commit e811d0b

Please sign in to comment.