fix(build): explicitly use ktfmt Google style
This commit is contained in:
parent
bdc5960a56
commit
7b3172e99d
|
@ -1,14 +1,12 @@
|
|||
plugins {
|
||||
alias(libs.plugins.spotless)
|
||||
}
|
||||
plugins { alias(libs.plugins.spotless) }
|
||||
|
||||
spotless {
|
||||
kotlin {
|
||||
target("**/*.kt")
|
||||
ktfmt()
|
||||
ktfmt().googleStyle()
|
||||
}
|
||||
kotlinGradle {
|
||||
target("**/*.kts")
|
||||
ktfmt()
|
||||
ktfmt().googleStyle()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
plugins { `kotlin-dsl` }
|
||||
|
||||
dependencies {
|
||||
implementation(libs.kotlin.gradle.plugin)
|
||||
}
|
||||
dependencies { implementation(libs.kotlin.gradle.plugin) }
|
||||
|
|
|
@ -9,11 +9,7 @@ pluginManagement {
|
|||
|
||||
dependencyResolutionManagement {
|
||||
repositories { mavenCentral() }
|
||||
versionCatalogs {
|
||||
maybeCreate("libs").apply {
|
||||
from(files("../gradle/libs.versions.toml"))
|
||||
}
|
||||
}
|
||||
versionCatalogs { maybeCreate("libs").apply { from(files("../gradle/libs.versions.toml")) } }
|
||||
}
|
||||
|
||||
rootProject.name = "buildSrc"
|
||||
|
|
|
@ -4,6 +4,7 @@ plugins {
|
|||
}
|
||||
|
||||
group = "dev.msfjarvis.tracelog"
|
||||
|
||||
version = "1.0.0-SNAPSHOT"
|
||||
|
||||
kotlin {
|
||||
|
|
|
@ -37,32 +37,37 @@ import org.jetbrains.kotlin.name.FqName
|
|||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
public class DebugLogTransformer(
|
||||
private val pluginContext: IrPluginContext,
|
||||
private val annotationClass: IrClassSymbol,
|
||||
private val logFunction: IrSimpleFunctionSymbol,
|
||||
private val pluginContext: IrPluginContext,
|
||||
private val annotationClass: IrClassSymbol,
|
||||
private val logFunction: IrSimpleFunctionSymbol,
|
||||
) : IrElementTransformerVoidWithContext() {
|
||||
private val typeUnit = pluginContext.irBuiltIns.unitType
|
||||
private val typeThrowable = pluginContext.irBuiltIns.throwableType
|
||||
|
||||
private val classMonotonic =
|
||||
pluginContext.referenceClass(
|
||||
ClassId(KOTLIN_TIME_FQNAME, Name.identifier("TimeSource.Monotonic")))!!
|
||||
pluginContext.referenceClass(
|
||||
ClassId(KOTLIN_TIME_FQNAME, Name.identifier("TimeSource.Monotonic"))
|
||||
)!!
|
||||
|
||||
private val funMarkNow =
|
||||
pluginContext
|
||||
.referenceFunctions(
|
||||
CallableId(
|
||||
KOTLIN_TIME_FQNAME.child(Name.identifier("TimeSource")),
|
||||
Name.identifier("markNow")))
|
||||
.single()
|
||||
pluginContext
|
||||
.referenceFunctions(
|
||||
CallableId(
|
||||
KOTLIN_TIME_FQNAME.child(Name.identifier("TimeSource")),
|
||||
Name.identifier("markNow")
|
||||
)
|
||||
)
|
||||
.single()
|
||||
|
||||
private val funElapsedNow =
|
||||
pluginContext
|
||||
.referenceFunctions(
|
||||
CallableId(
|
||||
KOTLIN_TIME_FQNAME.child(Name.identifier("TimeMark")),
|
||||
Name.identifier("elapsedNow")))
|
||||
.single()
|
||||
pluginContext
|
||||
.referenceFunctions(
|
||||
CallableId(
|
||||
KOTLIN_TIME_FQNAME.child(Name.identifier("TimeMark")),
|
||||
Name.identifier("elapsedNow")
|
||||
)
|
||||
)
|
||||
.single()
|
||||
|
||||
override fun visitFunctionNew(declaration: IrFunction): IrStatement {
|
||||
val body = declaration.body
|
||||
|
@ -86,14 +91,15 @@ public class DebugLogTransformer(
|
|||
}
|
||||
|
||||
private fun IrBuilderWithScope.irDebugExit(
|
||||
function: IrFunction,
|
||||
startTime: IrValueDeclaration,
|
||||
result: IrExpression? = null
|
||||
function: IrFunction,
|
||||
startTime: IrValueDeclaration,
|
||||
result: IrExpression? = null
|
||||
): IrCall {
|
||||
val concat = irConcat()
|
||||
concat.addArgument(irString("⇠ ${function.name} ["))
|
||||
concat.addArgument(
|
||||
irCall(funElapsedNow).also { call -> call.dispatchReceiver = irGet(startTime) })
|
||||
irCall(funElapsedNow).also { call -> call.dispatchReceiver = irGet(startTime) }
|
||||
)
|
||||
if (result != null) {
|
||||
concat.addArgument(irString("] = "))
|
||||
concat.addArgument(result)
|
||||
|
@ -109,43 +115,44 @@ public class DebugLogTransformer(
|
|||
+irDebugEnter(function)
|
||||
|
||||
val startTime =
|
||||
irTemporary(
|
||||
irCall(funMarkNow).also { call ->
|
||||
call.dispatchReceiver = irGetObject(classMonotonic)
|
||||
})
|
||||
irTemporary(
|
||||
irCall(funMarkNow).also { call -> call.dispatchReceiver = irGetObject(classMonotonic) }
|
||||
)
|
||||
|
||||
val tryBlock =
|
||||
irBlock(resultType = function.returnType) {
|
||||
for (statement in body.statements) +statement
|
||||
if (function.returnType == typeUnit) +irDebugExit(function, startTime)
|
||||
}
|
||||
.transform(DebugLogReturnTransformer(function, startTime), null)
|
||||
irBlock(resultType = function.returnType) {
|
||||
for (statement in body.statements) +statement
|
||||
if (function.returnType == typeUnit) +irDebugExit(function, startTime)
|
||||
}
|
||||
.transform(DebugLogReturnTransformer(function, startTime), null)
|
||||
|
||||
val throwable =
|
||||
buildVariable(
|
||||
scope.getLocalDeclarationParent(),
|
||||
startOffset,
|
||||
endOffset,
|
||||
IrDeclarationOrigin.CATCH_PARAMETER,
|
||||
Name.identifier("t"),
|
||||
typeThrowable)
|
||||
buildVariable(
|
||||
scope.getLocalDeclarationParent(),
|
||||
startOffset,
|
||||
endOffset,
|
||||
IrDeclarationOrigin.CATCH_PARAMETER,
|
||||
Name.identifier("t"),
|
||||
typeThrowable
|
||||
)
|
||||
|
||||
+IrTryImpl(startOffset, endOffset, tryBlock.type).also { irTry ->
|
||||
irTry.tryResult = tryBlock
|
||||
irTry.catches +=
|
||||
irCatch(
|
||||
throwable,
|
||||
irBlock {
|
||||
+irDebugExit(function, startTime, irGet(throwable))
|
||||
+irThrow(irGet(throwable))
|
||||
})
|
||||
irCatch(
|
||||
throwable,
|
||||
irBlock {
|
||||
+irDebugExit(function, startTime, irGet(throwable))
|
||||
+irThrow(irGet(throwable))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inner class DebugLogReturnTransformer(
|
||||
private val function: IrFunction,
|
||||
private val startTime: IrVariable
|
||||
private val function: IrFunction,
|
||||
private val startTime: IrVariable
|
||||
) : IrElementTransformerVoidWithContext() {
|
||||
override fun visitReturn(expression: IrReturn): IrExpression {
|
||||
if (expression.returnTargetSymbol != function.symbol) return super.visitReturn(expression)
|
||||
|
|
|
@ -9,8 +9,9 @@ import org.jetbrains.kotlin.config.CompilerConfiguration
|
|||
import org.jetbrains.kotlin.config.CompilerConfigurationKey
|
||||
|
||||
internal val LOGGER_FUNCTION =
|
||||
CompilerConfigurationKey<String>(
|
||||
"Logger function invoked to dump function metadata. Must have a single parameter of type `Any?`")
|
||||
CompilerConfigurationKey<String>(
|
||||
"Logger function invoked to dump function metadata. Must have a single parameter of type `Any?`"
|
||||
)
|
||||
|
||||
@OptIn(ExperimentalCompilerApi::class)
|
||||
@AutoService(CommandLineProcessor::class)
|
||||
|
@ -18,21 +19,22 @@ public class TracingCommandLineProcessor : CommandLineProcessor {
|
|||
|
||||
internal companion object {
|
||||
val OPTION_LOGGER_FUNCTION =
|
||||
CliOption(
|
||||
optionName = "loggerFunction",
|
||||
valueDescription = "kotlin.io.println",
|
||||
description = LOGGER_FUNCTION.toString(),
|
||||
required = false,
|
||||
allowMultipleOccurrences = false)
|
||||
CliOption(
|
||||
optionName = "loggerFunction",
|
||||
valueDescription = "kotlin.io.println",
|
||||
description = LOGGER_FUNCTION.toString(),
|
||||
required = false,
|
||||
allowMultipleOccurrences = false
|
||||
)
|
||||
}
|
||||
|
||||
override val pluginId: String = "dev.msfjarvis.tracelog"
|
||||
override val pluginOptions: Collection<AbstractCliOption> = listOf(OPTION_LOGGER_FUNCTION)
|
||||
|
||||
override fun processOption(
|
||||
option: AbstractCliOption,
|
||||
value: String,
|
||||
configuration: CompilerConfiguration
|
||||
option: AbstractCliOption,
|
||||
value: String,
|
||||
configuration: CompilerConfiguration
|
||||
) {
|
||||
when (option.optionName) {
|
||||
"loggerFunction" -> configuration.put(LOGGER_FUNCTION, value)
|
||||
|
|
|
@ -16,10 +16,11 @@ public class TracingCompilerPluginRegistrar : CompilerPluginRegistrar() {
|
|||
|
||||
override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) {
|
||||
val messageCollector =
|
||||
configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.NONE)
|
||||
configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.NONE)
|
||||
val loggerFunction = configuration[LOGGER_FUNCTION] ?: "kotlin.io.println"
|
||||
|
||||
IrGenerationExtension.registerExtension(
|
||||
TracingIrGenerationExtension(messageCollector, loggerFunction))
|
||||
TracingIrGenerationExtension(messageCollector, loggerFunction)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,44 +12,49 @@ import org.jetbrains.kotlin.name.FqName
|
|||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
public class TracingIrGenerationExtension(
|
||||
private val messageCollector: MessageCollector,
|
||||
private val loggerFunction: String
|
||||
private val messageCollector: MessageCollector,
|
||||
private val loggerFunction: String
|
||||
) : IrGenerationExtension {
|
||||
|
||||
override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {
|
||||
val debugLogAnnotation =
|
||||
pluginContext.referenceClass(
|
||||
ClassId(
|
||||
FqName("${BuildConfig.KOTLIN_PLUGIN_GROUP}.runtime.annotations"),
|
||||
Name.identifier("DebugLog"),
|
||||
))
|
||||
pluginContext.referenceClass(
|
||||
ClassId(
|
||||
FqName("${BuildConfig.KOTLIN_PLUGIN_GROUP}.runtime.annotations"),
|
||||
Name.identifier("DebugLog"),
|
||||
)
|
||||
)
|
||||
if (debugLogAnnotation == null) {
|
||||
messageCollector.report(CompilerMessageSeverity.ERROR, "Failed to find 'DebugLog' annotation")
|
||||
return
|
||||
}
|
||||
val loggerFun =
|
||||
pluginContext.referenceFunctions(loggerFunction.toCallableId()).firstOrNull {
|
||||
val parameters = it.owner.valueParameters
|
||||
parameters.size == 1 && parameters[0].type.isNullableAny()
|
||||
}
|
||||
pluginContext.referenceFunctions(loggerFunction.toCallableId()).firstOrNull {
|
||||
val parameters = it.owner.valueParameters
|
||||
parameters.size == 1 && parameters[0].type.isNullableAny()
|
||||
}
|
||||
if (loggerFun == null) {
|
||||
messageCollector.report(
|
||||
CompilerMessageSeverity.ERROR, "Failed to resolve logger method '$loggerFunction'")
|
||||
CompilerMessageSeverity.ERROR,
|
||||
"Failed to resolve logger method '$loggerFunction'"
|
||||
)
|
||||
return
|
||||
}
|
||||
moduleFragment.transform(
|
||||
DebugLogTransformer(pluginContext, debugLogAnnotation, loggerFun), null)
|
||||
DebugLogTransformer(pluginContext, debugLogAnnotation, loggerFun),
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
private fun String.toCallableId(): CallableId {
|
||||
val parts = split(".")
|
||||
val methodName = parts.last()
|
||||
val fqName =
|
||||
if (parts.size == 1) {
|
||||
FqName("")
|
||||
} else {
|
||||
FqName((parts - methodName).joinToString("."))
|
||||
}
|
||||
if (parts.size == 1) {
|
||||
FqName("")
|
||||
} else {
|
||||
FqName((parts - methodName).joinToString("."))
|
||||
}
|
||||
return CallableId(fqName, Name.identifier(methodName))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,9 +17,9 @@ class DebugLogTransformerTest {
|
|||
@Test
|
||||
fun `compiler plugin successfully transforms code`() {
|
||||
val srcFile =
|
||||
kotlin(
|
||||
"SourceFile.kt",
|
||||
"""
|
||||
kotlin(
|
||||
"SourceFile.kt",
|
||||
"""
|
||||
import ${BuildConfig.KOTLIN_PLUGIN_GROUP}.runtime.annotations.DebugLog
|
||||
public val messages = mutableListOf<String>()
|
||||
fun recordMessage(message: Any?) {
|
||||
|
@ -52,31 +52,32 @@ class DebugLogTransformerTest {
|
|||
}
|
||||
}
|
||||
"""
|
||||
.trimIndent())
|
||||
.trimIndent()
|
||||
)
|
||||
|
||||
val result =
|
||||
KotlinCompilation()
|
||||
.apply {
|
||||
val processor = TracingCommandLineProcessor()
|
||||
pluginOptions = buildList {
|
||||
add(
|
||||
processor.option(
|
||||
TracingCommandLineProcessor.OPTION_LOGGER_FUNCTION, "recordMessage"))
|
||||
}
|
||||
sources = listOf(srcFile)
|
||||
compilerPluginRegistrars = listOf(TracingCompilerPluginRegistrar())
|
||||
commandLineProcessors = listOf(processor)
|
||||
noOptimize = true
|
||||
KotlinCompilation()
|
||||
.apply {
|
||||
val processor = TracingCommandLineProcessor()
|
||||
pluginOptions = buildList {
|
||||
add(
|
||||
processor.option(TracingCommandLineProcessor.OPTION_LOGGER_FUNCTION, "recordMessage")
|
||||
)
|
||||
}
|
||||
sources = listOf(srcFile)
|
||||
compilerPluginRegistrars = listOf(TracingCompilerPluginRegistrar())
|
||||
commandLineProcessors = listOf(processor)
|
||||
noOptimize = true
|
||||
|
||||
inheritClassPath = true
|
||||
messageOutputStream = System.out
|
||||
}
|
||||
.compile()
|
||||
inheritClassPath = true
|
||||
messageOutputStream = System.out
|
||||
}
|
||||
.compile()
|
||||
assertEquals(ExitCode.OK, result.exitCode)
|
||||
|
||||
val kClazz = result.classLoader.loadClass("SourceFileKt")
|
||||
val transformableWithReturnValue =
|
||||
kClazz.declaredMethods.first { it.name == "transformableWithReturnValue" }
|
||||
kClazz.declaredMethods.first { it.name == "transformableWithReturnValue" }
|
||||
val retVal = transformableWithReturnValue.invoke(null)
|
||||
assertThat(retVal).isInstanceOf(String::class.java)
|
||||
assertThat(retVal).isEqualTo("Return value!")
|
||||
|
@ -86,9 +87,9 @@ class DebugLogTransformerTest {
|
|||
assertThat(messages).isNotNull()
|
||||
assertThat(messages).contains("⇢ transformableWithReturnValue()")
|
||||
assertThat((messages?.last() as? String)?.replace("\\[.*]".toRegex(), "[]"))
|
||||
.isEqualTo(
|
||||
"⇠ transformableWithReturnValue [] = Return value!",
|
||||
)
|
||||
.isEqualTo(
|
||||
"⇠ transformableWithReturnValue [] = Return value!",
|
||||
)
|
||||
}
|
||||
|
||||
private fun CommandLineProcessor.option(key: CliOption, value: Any?): PluginOption {
|
||||
|
|
|
@ -12,7 +12,7 @@ import org.jetbrains.kotlin.gradle.plugin.SubpluginOption
|
|||
public class TraceLogGradlePlugin : KotlinCompilerPluginSupportPlugin {
|
||||
|
||||
override fun applyToCompilation(
|
||||
kotlinCompilation: KotlinCompilation<*>
|
||||
kotlinCompilation: KotlinCompilation<*>
|
||||
): Provider<List<SubpluginOption>> {
|
||||
return kotlinCompilation.target.project.provider {
|
||||
buildList { add(SubpluginOption(key = "loggerFunction", value = "kotlin.io.println")) }
|
||||
|
@ -22,11 +22,11 @@ public class TraceLogGradlePlugin : KotlinCompilerPluginSupportPlugin {
|
|||
override fun getCompilerPluginId(): String = BuildConfig.KOTLIN_PLUGIN_GROUP
|
||||
|
||||
override fun getPluginArtifact(): SubpluginArtifact =
|
||||
SubpluginArtifact(
|
||||
groupId = BuildConfig.KOTLIN_PLUGIN_GROUP,
|
||||
artifactId = BuildConfig.KOTLIN_PLUGIN_NAME,
|
||||
version = BuildConfig.KOTLIN_PLUGIN_VERSION,
|
||||
)
|
||||
SubpluginArtifact(
|
||||
groupId = BuildConfig.KOTLIN_PLUGIN_GROUP,
|
||||
artifactId = BuildConfig.KOTLIN_PLUGIN_NAME,
|
||||
version = BuildConfig.KOTLIN_PLUGIN_VERSION,
|
||||
)
|
||||
|
||||
override fun isApplicable(kotlinCompilation: KotlinCompilation<*>): Boolean = true
|
||||
}
|
||||
|
|
|
@ -1,3 +1 @@
|
|||
plugins {
|
||||
`tracelog-kotlin-library`
|
||||
}
|
||||
plugins { `tracelog-kotlin-library` }
|
||||
|
|
|
@ -5,9 +5,7 @@ plugins {
|
|||
application
|
||||
}
|
||||
|
||||
application {
|
||||
mainClass.set("dev.msfjarvis.tracelog.sample.MainKt")
|
||||
}
|
||||
application { mainClass.set("dev.msfjarvis.tracelog.sample.MainKt") }
|
||||
|
||||
tasks.withType<KotlinCompile>().configureEach {
|
||||
compilerOptions.freeCompilerArgs.addAll(
|
||||
|
|
|
@ -12,8 +12,11 @@ private const val MATCH_PARAMS = "params"
|
|||
private const val MATCH_TIME = "time"
|
||||
private const val MATCH_RETURN = "retval"
|
||||
|
||||
private val ENTER_REGEX = "(?<$MATCH_ARROW>⇢) (?<$MATCH_IDENTIFIER>[a-zA-Z].*)\\((?<$MATCH_PARAMS>.*)\\)".toRegex()
|
||||
private val EXIT_REGEX = "(?<$MATCH_ARROW>⇠) (?<$MATCH_IDENTIFIER>[a-zA-Z].*) \\[(?<$MATCH_TIME>.*)] = (?<$MATCH_RETURN>.*)".toRegex()
|
||||
private val ENTER_REGEX =
|
||||
"(?<$MATCH_ARROW>⇢) (?<$MATCH_IDENTIFIER>[a-zA-Z].*)\\((?<$MATCH_PARAMS>.*)\\)".toRegex()
|
||||
private val EXIT_REGEX =
|
||||
"(?<$MATCH_ARROW>⇠) (?<$MATCH_IDENTIFIER>[a-zA-Z].*) \\[(?<$MATCH_TIME>.*)] = (?<$MATCH_RETURN>.*)"
|
||||
.toRegex()
|
||||
|
||||
private fun MatchResult.getMatch(groupName: String): String {
|
||||
return groups[groupName]!!.value
|
||||
|
@ -25,38 +28,43 @@ fun main() {
|
|||
messages.forEach { msg ->
|
||||
var matches = ENTER_REGEX.find(msg)
|
||||
if (matches != null) {
|
||||
val params = matches.getMatch(MATCH_PARAMS).split(", ").map {
|
||||
val split = it.split("=")
|
||||
(split[0] to split[1])
|
||||
}.joinToString(", ") { (name, value) ->
|
||||
"${brightYellow(name)}=${brightBlue(value)}"
|
||||
}
|
||||
t.println("""
|
||||
val params =
|
||||
matches
|
||||
.getMatch(MATCH_PARAMS)
|
||||
.split(", ")
|
||||
.map {
|
||||
val split = it.split("=")
|
||||
(split[0] to split[1])
|
||||
}
|
||||
.joinToString(", ") { (name, value) -> "${brightYellow(name)}=${brightBlue(value)}" }
|
||||
t.println(
|
||||
"""
|
||||
${red(matches.getMatch(MATCH_ARROW))} ${green(matches.getMatch(MATCH_IDENTIFIER))}($params)
|
||||
""".trimIndent())
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
} else {
|
||||
matches = EXIT_REGEX.matchEntire(msg)
|
||||
if (matches != null) {
|
||||
t.println("""
|
||||
t.println(
|
||||
"""
|
||||
${red(matches.getMatch(MATCH_ARROW))} ${green(matches.getMatch(MATCH_IDENTIFIER))} [${blue(matches.getMatch(
|
||||
MATCH_TIME))}] = ${brightWhite(matches.getMatch(MATCH_RETURN))}
|
||||
""".trimIndent())
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom implementation of a logger function for use by the compiler plugin.
|
||||
*/
|
||||
/** Custom implementation of a logger function for use by the compiler plugin. */
|
||||
@Suppress("Unused") // Used by the generated bytecode
|
||||
fun recordMessage(message: Any?) {
|
||||
messages += message.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* An example of an annotated method which will be transformed by the compiler plugin.
|
||||
*/
|
||||
/** An example of an annotated method which will be transformed by the compiler plugin. */
|
||||
@DebugLog
|
||||
fun debuggableFunction(p0: String, p1: String = "Bar"): String {
|
||||
return "Debugging is cool!"
|
||||
|
|
Loading…
Reference in New Issue