From 98640133b71ad7d2a9244aa13fa6646c74db6656 Mon Sep 17 00:00:00 2001 From: Brian Parent Date: Fri, 3 Nov 2023 20:16:37 -0500 Subject: [PATCH 1/7] Move to Version catalog and use gradle plugin conviences --- build.gradle | 40 ++----------------- gradle/libs.versions.toml | 23 +++++++++++ mr-clean-annotations/build.gradle | 8 ++-- mr-clean-plugin/build.gradle | 35 +++++++++++----- .../com.trello.mrclean.properties | 1 - mr-clean-processor/build.gradle | 24 ++++++----- 6 files changed, 69 insertions(+), 62 deletions(-) create mode 100644 gradle/libs.versions.toml delete mode 100644 mr-clean-plugin/src/main/resources/META-INF/gradle-plugins/com.trello.mrclean.properties diff --git a/build.gradle b/build.gradle index bc9e2ea..6a4dbc6 100644 --- a/build.gradle +++ b/build.gradle @@ -1,39 +1,4 @@ buildscript { - ext.versions = [ - 'incap' : '1.0.0', - 'kotlin': '1.9.0' - ] - - ext.deps = [ - android: [ - 'runtime': 'com.google.android:android:4.1.1.4', - 'gradlePlugin': "com.android.tools.build:gradle:8.1.0", - ], - 'autoService':'com.google.auto.service:auto-service:1.1.1', - 'kotlin': [ - 'reflect': "org.jetbrains.kotlin:kotlin-reflect:${versions.kotlin}", - 'stdlib': "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${versions.kotlin}" - ], - 'incap': [ - 'runtime': "net.ltgt.gradle.incap:incap:${versions.incap}", - 'processor': "net.ltgt.gradle.incap:incap-processor:${versions.incap}", - ], - 'junit': 'junit:junit:4.13.2', - 'kotlinx': [ - 'metadataJvm':"org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.7.0" - ], - 'kotlinpoet': 'com.squareup:kotlinpoet:1.12.0' - ] - - repositories { - mavenCentral() - } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}" - classpath "com.vanniktech:gradle-maven-publish-plugin:0.18.0" - } - allprojects { repositories { google() @@ -48,5 +13,8 @@ buildscript { plugins { - id "com.github.ben-manes.versions" version "0.45.0" + alias libs.plugins.kotlin.jvm apply false + alias libs.plugins.kotlin.kapt apply false + alias libs.plugins.vanniktech.publish apply false + alias libs.plugins.versions } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 0000000..59d6234 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,23 @@ +[versions] +incap = "1.0.0" +kotlin = "1.9.20" +#ksp = +versionsPlugin = "0.49.0" + +[libraries] +android-runtime = "com.google.android:android:4.1.1.4" +android-gradlePlugin = "com.android.tools.build:gradle:8.1.2" +autoService = "com.google.auto.service:auto-service:1.1.1" +incap-runtime = { module = "net.ltgt.gradle.incap:incap", version.ref = "incap" } +incap-processor = { module = "net.ltgt.gradle.incap:incap-processor", version.ref = "incap" } +junit = "junit:junit:4.13.2" +kotlinpoet = "com.squareup:kotlinpoet:1.14.2" +kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" } +kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk7", version.ref = "kotlin" } +kotlinx-metadataJvm = "org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.7.0" + +[plugins] +versions = { id = "com.github.ben-manes.versions", version.ref = "versionsPlugin" } +kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } +kotlin-kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" } +vanniktech-publish = { id = "com.vanniktech.maven.publish", version = "0.25.3" } \ No newline at end of file diff --git a/mr-clean-annotations/build.gradle b/mr-clean-annotations/build.gradle index 4ad8b97..166da83 100644 --- a/mr-clean-annotations/build.gradle +++ b/mr-clean-annotations/build.gradle @@ -1,11 +1,13 @@ -apply plugin: 'kotlin' -apply plugin: 'com.vanniktech.maven.publish' +plugins{ + alias libs.plugins.kotlin.jvm + alias libs.plugins.vanniktech.publish +} repositories { mavenCentral() } dependencies { - implementation deps.kotlin.stdlib + implementation libs.kotlin.stdlib } sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 diff --git a/mr-clean-plugin/build.gradle b/mr-clean-plugin/build.gradle index cac3d04..852c8cb 100644 --- a/mr-clean-plugin/build.gradle +++ b/mr-clean-plugin/build.gradle @@ -1,22 +1,33 @@ -apply plugin: 'java-gradle-plugin' -apply plugin: 'kotlin' -apply plugin: 'com.vanniktech.maven.publish' +plugins { + alias libs.plugins.kotlin.jvm + alias libs.plugins.vanniktech.publish + id 'java-gradle-plugin' +} dependencies { compileOnly gradleApi() implementation project(':mr-clean-annotations') - implementation deps.android.gradlePlugin - implementation deps.kotlin.stdlib - implementation deps.kotlinpoet + implementation libs.android.gradlePlugin + implementation libs.kotlin.stdlib + implementation libs.kotlinpoet - testImplementation deps.junit + testImplementation libs.junit } sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 sourceSets.main.java.srcDir "$buildDir/generated/source/mrclean" -task pluginVersion { +gradlePlugin { + plugins { + mrclean { + id = 'com.trello.mrclean' + implementationClass = 'com.trello.mrclean.plugin.MrCleanPlugin' + } + } +} + +tasks.register('pluginVersion') { def outputDir = file("$buildDir/generated/source/mrclean/") inputs.property 'version', version @@ -32,7 +43,9 @@ val VERSION = "${project.version}" """ } } - -tasks.getByName("pluginVersion").dependsOn("javaSourcesJar") -tasks.getByName('compileKotlin').dependsOn('pluginVersion') +afterEvaluate { + tasks.named('compileKotlin') { dependsOn('pluginVersion') } +// tasks.named('javaSourcesJar') { dependsOn('pluginVersion') } + tasks.named('sourcesJar') { dependsOn('pluginVersion') } +} diff --git a/mr-clean-plugin/src/main/resources/META-INF/gradle-plugins/com.trello.mrclean.properties b/mr-clean-plugin/src/main/resources/META-INF/gradle-plugins/com.trello.mrclean.properties deleted file mode 100644 index 1b74c7e..0000000 --- a/mr-clean-plugin/src/main/resources/META-INF/gradle-plugins/com.trello.mrclean.properties +++ /dev/null @@ -1 +0,0 @@ -implementation-class=com.trello.mrclean.plugin.MrCleanPlugin \ No newline at end of file diff --git a/mr-clean-processor/build.gradle b/mr-clean-processor/build.gradle index d80df17..fd25118 100644 --- a/mr-clean-processor/build.gradle +++ b/mr-clean-processor/build.gradle @@ -1,21 +1,23 @@ -apply plugin: 'kotlin' -apply plugin: 'kotlin-kapt' -apply plugin: 'com.vanniktech.maven.publish' +plugins { + alias libs.plugins.kotlin.jvm + alias libs.plugins.kotlin.kapt + alias libs.plugins.vanniktech.publish +} dependencies { compileOnly project(':mr-clean-annotations') - compileOnly deps.autoService - kapt deps.autoService + compileOnly libs.autoService + kapt libs.autoService - api deps.incap.runtime - compileOnly deps.incap.processor + api libs.incap.runtime + compileOnly libs.incap.processor - implementation deps.kotlin.stdlib + implementation libs.kotlin.stdlib - implementation deps.kotlinx.metadataJvm + implementation libs.kotlinx.metadataJvm - implementation deps.kotlinpoet + implementation libs.kotlinpoet - testImplementation deps.junit + testImplementation libs.junit } sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 From ee6cbe56638d290320facfe89946a541daae7df5 Mon Sep 17 00:00:00 2001 From: Brian Parent Date: Sat, 4 Nov 2023 22:16:05 -0500 Subject: [PATCH 2/7] Early first stab at ksp support --- build.gradle | 6 +- gradle.properties | 15 +- gradle/libs.versions.toml | 45 +++- gradle/wrapper/gradle-wrapper.properties | 2 +- mr-clean-annotations/gradle.properties | 3 - mr-clean-debug-processor/build.gradle.kts | 23 ++ mr-clean-debug-processor/gradle.properties | 3 + .../trello/mrclean/MrCleanDebugProcessor.kt | 29 +++ .../mrclean/MrCleanDebugProcessorProvider.kt | 16 ++ mr-clean-plugin/build.gradle | 8 +- .../mrclean/plugin/GenerateRootFunctions.kt | 26 +- .../trello/mrclean/plugin/MrCleanPlugin.kt | 72 ++++-- mr-clean-processor-core/build.gradle.kts | 18 ++ mr-clean-processor-core/gradle.properties | 3 + .../trello/mrclean/MrCleanCoreProcessor.kt | 145 +++++++++++ .../com/trello/mrclean/MrCleanDataClasses.kt | 20 ++ .../java/com/trello/mrclean/MrCleanVisitor.kt | 32 +++ .../com/trello/mrclean/SanitizeGenerator.kt | 45 ++++ .../trello/mrclean/SanitizeGeneratorTest.kt | 209 ++++++++++++++++ mr-clean-processor/build.gradle | 24 -- mr-clean-processor/build.gradle.kts | 26 ++ .../java/com/trello/mrclean/KotlinTypes.kt | 33 --- .../main/java/com/trello/mrclean/Metadata.kt | 225 ------------------ .../com/trello/mrclean/MrCleanProcessor.kt | 160 ++----------- .../mrclean/MrCleanProcessorProvider.kt | 16 ++ .../com/trello/mrclean/SanitizeGenerator.kt | 40 ---- .../trello/mrclean/SanitizeGeneratorTest.kt | 171 ------------- .../.gitignore | 0 .../build.gradle | 0 mr-clean-runtime/gradle.properties | 3 + .../trello/mrclean/annotations/Sanitize.kt | 1 + settings.gradle | 6 +- 32 files changed, 731 insertions(+), 694 deletions(-) delete mode 100644 mr-clean-annotations/gradle.properties create mode 100644 mr-clean-debug-processor/build.gradle.kts create mode 100644 mr-clean-debug-processor/gradle.properties create mode 100644 mr-clean-debug-processor/src/main/java/com/trello/mrclean/MrCleanDebugProcessor.kt create mode 100644 mr-clean-debug-processor/src/main/java/com/trello/mrclean/MrCleanDebugProcessorProvider.kt create mode 100644 mr-clean-processor-core/build.gradle.kts create mode 100644 mr-clean-processor-core/gradle.properties create mode 100644 mr-clean-processor-core/src/main/java/com/trello/mrclean/MrCleanCoreProcessor.kt create mode 100644 mr-clean-processor-core/src/main/java/com/trello/mrclean/MrCleanDataClasses.kt create mode 100644 mr-clean-processor-core/src/main/java/com/trello/mrclean/MrCleanVisitor.kt create mode 100644 mr-clean-processor-core/src/main/java/com/trello/mrclean/SanitizeGenerator.kt create mode 100644 mr-clean-processor-core/src/test/java/com/trello/mrclean/SanitizeGeneratorTest.kt delete mode 100644 mr-clean-processor/build.gradle create mode 100644 mr-clean-processor/build.gradle.kts delete mode 100644 mr-clean-processor/src/main/java/com/trello/mrclean/KotlinTypes.kt delete mode 100644 mr-clean-processor/src/main/java/com/trello/mrclean/Metadata.kt create mode 100644 mr-clean-processor/src/main/java/com/trello/mrclean/MrCleanProcessorProvider.kt delete mode 100644 mr-clean-processor/src/main/java/com/trello/mrclean/SanitizeGenerator.kt delete mode 100644 mr-clean-processor/src/test/java/com/trello/mrclean/SanitizeGeneratorTest.kt rename {mr-clean-annotations => mr-clean-runtime}/.gitignore (100%) rename {mr-clean-annotations => mr-clean-runtime}/build.gradle (100%) create mode 100644 mr-clean-runtime/gradle.properties rename {mr-clean-annotations => mr-clean-runtime}/src/main/java/com/trello/mrclean/annotations/Sanitize.kt (96%) diff --git a/build.gradle b/build.gradle index 6a4dbc6..39d98b0 100644 --- a/build.gradle +++ b/build.gradle @@ -3,6 +3,7 @@ buildscript { repositories { google() mavenCentral() + mavenLocal() jcenter() } @@ -14,7 +15,10 @@ buildscript { plugins { alias libs.plugins.kotlin.jvm apply false - alias libs.plugins.kotlin.kapt apply false + alias libs.plugins.ksp apply false alias libs.plugins.vanniktech.publish apply false alias libs.plugins.versions + alias(libs.plugins.com.android.application) apply false + alias(libs.plugins.org.jetbrains.kotlin.android) apply false } + diff --git a/gradle.properties b/gradle.properties index dec05e7..5ec04b3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.trello.mrclean -VERSION_NAME=1.2.3-SNAPSHOT +VERSION_NAME=2.0.0-SNAPSHOT POM_DESCRIPTION=Gradle plugin for generating release worthy toStrings POM_INCEPTION_YEAR=2018 @@ -17,4 +17,15 @@ POM_DEVELOPER_ID=atlassian POM_DEVELOPER_NAME=Atlassian Inc POM_DEVELOPER_URL=https://github.com/atlassian-labs android.defaults.buildfeatures.buildconfig=true -android.enableBuildConfigAsBytecode=true \ No newline at end of file +android.enableBuildConfigAsBytecode=true + +SONATYPE_HOST=DEFAULT + +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 59d6234..e096617 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,23 +1,44 @@ [versions] -incap = "1.0.0" -kotlin = "1.9.20" -#ksp = +activity-compose = "1.8.0" +autoservice = "1.1.1" +autoserviceKsp = "1.1.0" +kotlin = "1.9.10" +ksp = "1.9.10-1.0.13" versionsPlugin = "0.49.0" +kotlinPoet = "1.14.2" +agp = "8.1.2" +org-jetbrains-kotlin-android = "1.9.10" +junit = "4.13.2" +runner = "1.0.2" +espresso-core = "3.0.2" +appcompat-v7 = "28.0.0" +compose-bom = "2023.10.01" [libraries] +autoservice-ksp = { module = "dev.zacsweers.autoservice:auto-service-ksp", version.ref = "autoserviceKsp" } +autoservice-annotations = { module = "com.google.auto.service:auto-service-annotations", version.ref = "autoservice" } android-runtime = "com.google.android:android:4.1.1.4" -android-gradlePlugin = "com.android.tools.build:gradle:8.1.2" -autoService = "com.google.auto.service:auto-service:1.1.1" -incap-runtime = { module = "net.ltgt.gradle.incap:incap", version.ref = "incap" } -incap-processor = { module = "net.ltgt.gradle.incap:incap-processor", version.ref = "incap" } +android-gradlePlugin = { module = "com.android.tools.build:gradle", version.ref = "agp" } junit = "junit:junit:4.13.2" -kotlinpoet = "com.squareup:kotlinpoet:1.14.2" -kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" } +kotlinPoet-core = { module = "com.squareup:kotlinpoet", version.ref = "kotlinPoet" } +kotlinPoet-ksp = { module = "com.squareup:kotlinpoet-ksp", version.ref = "kotlinPoet" } kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk7", version.ref = "kotlin" } -kotlinx-metadataJvm = "org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.7.0" +ksp = { module = "com.google.devtools.ksp:symbol-processing-api ", version.ref = "ksp" } +plugin-ksp = { module = "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" } +junit-junit = { group = "junit", name = "junit", version.ref = "junit" } +runner = { group = "com.android.support.test", name = "runner", version.ref = "runner" } +espresso-core = { group = "com.android.support.test.espresso", name = "espresso-core", version.ref = "espresso-core" } +appcompat-v7 = { group = "com.android.support", name = "appcompat-v7", version.ref = "appcompat-v7" } + +compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" } +ui = { group = "androidx.compose.ui", name = "ui" } +material3 = { group = "androidx.compose.material3", name = "material3" } +activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activity-compose" } [plugins] versions = { id = "com.github.ben-manes.versions", version.ref = "versionsPlugin" } kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } -kotlin-kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" } -vanniktech-publish = { id = "com.vanniktech.maven.publish", version = "0.25.3" } \ No newline at end of file +vanniktech-publish = { id = "com.vanniktech.maven.publish", version = "0.25.3" } +ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } +com-android-application = { id = "com.android.application", version.ref = "agp" } +org-jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "org-jetbrains-kotlin-android" } \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index da1db5f..59bc51a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/mr-clean-annotations/gradle.properties b/mr-clean-annotations/gradle.properties deleted file mode 100644 index 9fb950a..0000000 --- a/mr-clean-annotations/gradle.properties +++ /dev/null @@ -1,3 +0,0 @@ -POM_NAME=Mr. Clean Annotations -POM_ARTIFACT_ID=mr-clean-annotations -POM_PACKAGING=jar \ No newline at end of file diff --git a/mr-clean-debug-processor/build.gradle.kts b/mr-clean-debug-processor/build.gradle.kts new file mode 100644 index 0000000..ed4d16d --- /dev/null +++ b/mr-clean-debug-processor/build.gradle.kts @@ -0,0 +1,23 @@ +plugins { + alias(libs.plugins.kotlin.jvm) + alias(libs.plugins.ksp) + alias(libs.plugins.vanniktech.publish) +} + +dependencies { + implementation(project(":mr-clean-processor-core")) + implementation(project(":mr-clean-runtime")) + ksp(libs.autoservice.ksp) + implementation(libs.autoservice.annotations) + + implementation(libs.kotlin.stdlib) + + implementation(libs.ksp) + + testImplementation(libs.junit) +} + +ksp { + arg("autoserviceKsp.verify", "true") + arg("autoserviceKsp.verbose", "true") +} diff --git a/mr-clean-debug-processor/gradle.properties b/mr-clean-debug-processor/gradle.properties new file mode 100644 index 0000000..443af23 --- /dev/null +++ b/mr-clean-debug-processor/gradle.properties @@ -0,0 +1,3 @@ +POM_NAME=Mr. Clean Debug Processor +POM_ARTIFACT_ID=mr-clean-debug-processor +POM_PACKAGING=jar \ No newline at end of file diff --git a/mr-clean-debug-processor/src/main/java/com/trello/mrclean/MrCleanDebugProcessor.kt b/mr-clean-debug-processor/src/main/java/com/trello/mrclean/MrCleanDebugProcessor.kt new file mode 100644 index 0000000..421d457 --- /dev/null +++ b/mr-clean-debug-processor/src/main/java/com/trello/mrclean/MrCleanDebugProcessor.kt @@ -0,0 +1,29 @@ +package com.trello.mrclean + +import com.google.devtools.ksp.processing.* +import com.google.devtools.ksp.symbol.* + +class MrCleanDebugProcessor( + val codeGenerator: CodeGenerator, + val options: Map, + private val logger: KSPLogger, +) : SymbolProcessor { + override fun process(resolver: Resolver): List { + logger.info("Mr. Clean Debug Processor has $options") + val debugOptions = mutableMapOf() + debugOptions.putAll(options) + if (options[MrCleanCoreProcessor.DEBUG_KEY] == null) { + logger.info("\"MrCleanCoreProcessor.DEBUG_KEY\" not set, defatuling to true") + debugOptions[MrCleanCoreProcessor.DEBUG_KEY] = "true" + } + if (options[MrCleanCoreProcessor.ROOT_GENERATOR_KEY] == null) { + logger.info("\"$MrCleanCoreProcessor.ROOT_GENERATOR_KEY\" not set, defaulting to true") + debugOptions[MrCleanCoreProcessor.ROOT_GENERATOR_KEY] = "true" + } + return MrCleanCoreProcessor( + codeGenerator = codeGenerator, + options = debugOptions, + logger = logger, + ).process(resolver) + } +} diff --git a/mr-clean-debug-processor/src/main/java/com/trello/mrclean/MrCleanDebugProcessorProvider.kt b/mr-clean-debug-processor/src/main/java/com/trello/mrclean/MrCleanDebugProcessorProvider.kt new file mode 100644 index 0000000..634fc36 --- /dev/null +++ b/mr-clean-debug-processor/src/main/java/com/trello/mrclean/MrCleanDebugProcessorProvider.kt @@ -0,0 +1,16 @@ +package com.trello.mrclean + +import com.google.auto.service.AutoService +import com.google.devtools.ksp.processing.SymbolProcessor +import com.google.devtools.ksp.processing.SymbolProcessorEnvironment +import com.google.devtools.ksp.processing.SymbolProcessorProvider + +@AutoService(SymbolProcessorProvider::class) +class MrCleanDebugProcessorProvider : SymbolProcessorProvider { + override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor = + MrCleanDebugProcessor( + codeGenerator = environment.codeGenerator, + options = environment.options, + logger = environment.logger, + ) +} diff --git a/mr-clean-plugin/build.gradle b/mr-clean-plugin/build.gradle index 852c8cb..f0a198e 100644 --- a/mr-clean-plugin/build.gradle +++ b/mr-clean-plugin/build.gradle @@ -5,11 +5,13 @@ plugins { } dependencies { compileOnly gradleApi() - implementation project(':mr-clean-annotations') + implementation project(':mr-clean-runtime') implementation libs.android.gradlePlugin + implementation libs.android.gradlePlugin + implementation libs.plugin.ksp implementation libs.kotlin.stdlib - implementation libs.kotlinpoet + implementation libs.kotlinPoet.core testImplementation libs.junit } @@ -45,7 +47,5 @@ val VERSION = "${project.version}" } afterEvaluate { tasks.named('compileKotlin') { dependsOn('pluginVersion') } -// tasks.named('javaSourcesJar') { dependsOn('pluginVersion') } tasks.named('sourcesJar') { dependsOn('pluginVersion') } } - diff --git a/mr-clean-plugin/src/main/java/com/trello/mrclean/plugin/GenerateRootFunctions.kt b/mr-clean-plugin/src/main/java/com/trello/mrclean/plugin/GenerateRootFunctions.kt index 5e266ac..259e861 100644 --- a/mr-clean-plugin/src/main/java/com/trello/mrclean/plugin/GenerateRootFunctions.kt +++ b/mr-clean-plugin/src/main/java/com/trello/mrclean/plugin/GenerateRootFunctions.kt @@ -1,28 +1,28 @@ package com.trello.mrclean.plugin import org.gradle.api.DefaultTask -import org.gradle.api.provider.Property import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.Property import org.gradle.api.tasks.Input import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction import java.io.File abstract class GenerateRootFunctions : DefaultTask() { - @get:OutputDirectory - abstract val outputDir: DirectoryProperty + @get:OutputDirectory + abstract val outputDir: DirectoryProperty - @get:Input - abstract val packageName: Property + @get:Input + abstract val packageName: Property - @Suppress("unused") // Invoked by Gradle. - @TaskAction - fun brewJava() { - brewJava(outputDir.asFile.get(), packageName.get()) - } + @Suppress("unused") // Invoked by Gradle. + @TaskAction + fun brewJava() { + brewJava(outputDir.asFile.get(), packageName.get()) + } } fun brewJava(outputDir: File, packageName: String) { - val compiler = RootFunctionGenerator() - compiler.createRootFunction(packageName).writeTo(outputDir) -} \ No newline at end of file + val compiler = RootFunctionGenerator() + compiler.createRootFunction(packageName).writeTo(outputDir) +} diff --git a/mr-clean-plugin/src/main/java/com/trello/mrclean/plugin/MrCleanPlugin.kt b/mr-clean-plugin/src/main/java/com/trello/mrclean/plugin/MrCleanPlugin.kt index 8c29a64..f8c86e5 100644 --- a/mr-clean-plugin/src/main/java/com/trello/mrclean/plugin/MrCleanPlugin.kt +++ b/mr-clean-plugin/src/main/java/com/trello/mrclean/plugin/MrCleanPlugin.kt @@ -5,6 +5,7 @@ import com.android.build.gradle.* import com.android.build.gradle.internal.manifest.parseManifest import com.android.builder.errors.EvalIssueException import com.android.builder.errors.IssueReporter +import com.google.devtools.ksp.gradle.KspExtension import com.trello.mrclean.VERSION import org.gradle.api.Plugin import org.gradle.api.Project @@ -23,39 +24,70 @@ class MrCleanPlugin : Plugin { } override fun apply(project: Project) { - project.plugins.apply("kotlin-kapt") - addKaptDeps(project) + project.plugins.apply("com.google.devtools.ksp") + val kspExtension = getOrCreateKsp(project, "ksp") + addKspDeps(project) val baseExtension = project.extensions.getByType(BaseExtension::class.java) val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java) + val buildTypeSet = mutableSetOf() + // we are generating the root here, so we don't need to generate it in the processor + kspExtension.arg("mrclean.rootgenerator", "false") androidComponents.onVariants { variant -> + val buildType = variant.buildType + if (buildType != null && !buildTypeSet.contains(buildType)) { + val isDebug = (variant.buildType == "debug") + val cleaned = buildType.replaceFirstChar { it.uppercaseChar() } + addMrCleanProcessor(project, cleaned, isDebug) + buildTypeSet.add(buildType) + } val packageName = getPackageNameBase(baseExtension) - variant.javaCompilation.annotationProcessor.arguments.put("mrclean.packagename", packageName) - variant.javaCompilation.annotationProcessor.arguments.put( - "mrclean.debug", - (variant.buildType == "debug").toString() - ) + kspExtension.arg("mrclean.packagename", packageName) val taskName = "generate${variant.name.capitalize()}RootSanitizeFunction" val outputDir = project.buildDir.resolve("generated/source/mrclean/${variant.name}") - log.debug("MrClean: task $taskName using directory $outputDir") + log.info("MrClean: task $taskName using directory $outputDir") val task = project.tasks.register(taskName, GenerateRootFunctions::class.java) { it.outputDir.set(outputDir) it.packageName.set(packageName) - } - variant.sources.assets?.addGeneratedSourceDirectory(task, GenerateRootFunctions::outputDir) + log.info("MrClean: setting ${variant.sources.java} to ${GenerateRootFunctions::outputDir}") + variant.sources.java?.addGeneratedSourceDirectory( + task, + GenerateRootFunctions::outputDir, + ) } } - private fun addKaptDeps(project: Project) { + private fun getOrCreateKsp(project: Project, name: String): KspExtension { + val hasKsp = project.extensions.findByName(name) != null + return if (hasKsp) { + log.info("MrClean: found $name") + project.extensions.getByName(name) as KspExtension +// project.extensions.getByType(KspExtension::class.java) + } else { + log.info("MrClean: trying to create $name") + project.extensions.create(name, KspExtension::class.java) + } + } + + private fun addKspDeps(project: Project) { val implDeps = project.configurations.getByName("implementation").dependencies - implDeps.add(project.dependencies.create("com.trello.mrclean:mr-clean-annotations:$VERSION")) - val kaptDeps = project.configurations.getByName("kapt").dependencies - kaptDeps.add(project.dependencies.create("com.trello.mrclean:mr-clean-processor:$VERSION")) + implDeps.add(project.dependencies.create("com.trello.mrclean:mr-clean-runtime:$VERSION")) + } + + private fun addMrCleanProcessor(project: Project, configuration: String, isDebug: Boolean) { + val coordinates = if (isDebug) { + "com.trello.mrclean:mr-clean-debug-processor" + } else { + "com.trello.mrclean:mr-clean-processor" + } + log.info("Mr Clean is adding: ksp$configuration $coordinates:$VERSION") + val kspDep = project.configurations.getByName("ksp$configuration").dependencies + kspDep.add(project.dependencies.create("$coordinates:$VERSION")) } private fun getPackageNameBase(extension: BaseExtension): String { return if (extension.namespace != null) { - log.debug("using namespace ${extension.namespace}") + log.info("using namespace ${extension.namespace}") extension.namespace!! } else { log.debug("couldn't find a namespace") @@ -79,10 +111,14 @@ class MrCleanPlugin : Plugin { BooleanSupplier { true }, object : IssueReporter() { override fun hasIssue(type: Type) = false - override fun reportIssue(type: Type, severity: Severity, exception: EvalIssueException) = + override fun reportIssue( + type: Type, + severity: Severity, + exception: EvalIssueException, + ) = throw exception - }).packageName - + }, + ).packageName } private operator fun ExtensionContainer.get(type: KClass): T { diff --git a/mr-clean-processor-core/build.gradle.kts b/mr-clean-processor-core/build.gradle.kts new file mode 100644 index 0000000..d24d3e2 --- /dev/null +++ b/mr-clean-processor-core/build.gradle.kts @@ -0,0 +1,18 @@ +plugins { + alias(libs.plugins.kotlin.jvm) + alias(libs.plugins.vanniktech.publish) +} + +dependencies { + + implementation(project(":mr-clean-runtime")) + implementation(libs.kotlin.stdlib) + + implementation(libs.kotlinPoet.core) + implementation(libs.kotlinPoet.ksp) + + implementation(libs.ksp) + + testImplementation(libs.junit) +} + diff --git a/mr-clean-processor-core/gradle.properties b/mr-clean-processor-core/gradle.properties new file mode 100644 index 0000000..1bec637 --- /dev/null +++ b/mr-clean-processor-core/gradle.properties @@ -0,0 +1,3 @@ +POM_NAME=Mr. Clean Processor +POM_ARTIFACT_ID=mr-clean-processor-core +POM_PACKAGING=jar \ No newline at end of file diff --git a/mr-clean-processor-core/src/main/java/com/trello/mrclean/MrCleanCoreProcessor.kt b/mr-clean-processor-core/src/main/java/com/trello/mrclean/MrCleanCoreProcessor.kt new file mode 100644 index 0000000..acfb515 --- /dev/null +++ b/mr-clean-processor-core/src/main/java/com/trello/mrclean/MrCleanCoreProcessor.kt @@ -0,0 +1,145 @@ +package com.trello.mrclean + +import com.google.devtools.ksp.processing.CodeGenerator +import com.google.devtools.ksp.processing.KSPLogger +import com.google.devtools.ksp.processing.Resolver +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.validate +import com.squareup.kotlinpoet.FileSpec +import com.squareup.kotlinpoet.FunSpec +import com.squareup.kotlinpoet.KModifier +import com.squareup.kotlinpoet.ksp.writeTo +import com.trello.mrclean.annotations.Sanitize + +class MrCleanCoreProcessor( + val codeGenerator: CodeGenerator, + val options: Map, + private val logger: KSPLogger, +) { + fun process(resolver: Resolver): List { + logger.info("Mr. Clean Core Processor has $options") + + val packageName = options[PACKAGE_KEY] + + if (packageName == null) { + logger.error("Mr. Clean didn't get a value for package name, a package must be passed in with the key \"mrclean.packagename\"") + return emptyList() + } + val annotationName = Sanitize::class.qualifiedName!! + val unfilteredSymbols = resolver.getSymbolsWithAnnotation(annotationName) + val symbolsNotProcessed = unfilteredSymbols.filterNot { it.validate(symbolPredicate) }.toList() + val symbols = unfilteredSymbols.filterIsInstance().filter { it.validate(symbolPredicate) } + + if (!symbols.iterator().hasNext()) { + logger.info("Mr. Clean found no symbols to process, exiting") + return symbolsNotProcessed + } + + val rootFromOpts = options[ROOT_GENERATOR_KEY] + val debugFromOpts = options[DEBUG_KEY] + + if (rootFromOpts == null) { + logger.info("Mr. Clean defaulting to not generating a root function. Can overrider with \"$ROOT_GENERATOR_KEY\"") + } + + if (debugFromOpts == null) { + logger.info("Mr. Clean defaulting to sanitization. Can be overriden with \"$DEBUG_KEY\"") + } + + val isDebug = debugFromOpts?.toBoolean() ?: false + val generateRoot = options[ROOT_GENERATOR_KEY]?.toBoolean() ?: false + + var symbolCount = 0 + val classes = mutableListOf() + symbols.forEach { + val visitor = MrCleanVisitor(logger) + it.accept(visitor, Unit) + classes.add( + MrCleanClassData( + qualifiedName = it.qualifiedName?.asString()!!, + simpleName = it.simpleName.asString(), + properties = visitor.properties, + enclosingPackage = it.packageName.getShortName() + .replaceFirstChar { first -> first.uppercase() }, + qualifiedPackage = it.packageName.asString(), + originatingFile = it.containingFile, + ), + ) + symbolCount++ + } + + val map = classes.map { + it to SanitizeGenerator.generateSanitizedToString( + it.qualifiedName, + it.simpleName, + it.properties, + isDebug, + ) + } + map.map { (classData, funSpec) -> + val fileSpec = FileSpec.builder( + packageName, + classData.getFileName(), + ) + .addFunction(funSpec) + .build() + + val originatingFiles = if (classData.originatingFile == null) { + emptyList() + } else { + listOf(classData.originatingFile) + } + fileSpec.writeTo( + codeGenerator = codeGenerator, + aggregating = false, + originatingKSFiles = originatingFiles, + ) + } + if (generateRoot) { + logger.info("Mr. Clean generating root function Any.santizedToString()") + createRootFunction(packageName).writeTo(codeGenerator, false) + } + logger.info("Mr. Clean processed $symbolCount symbols returning ${symbolsNotProcessed.size}") + return symbolsNotProcessed + } + + private val symbolPredicate: (data: KSNode?, declaration: KSNode) -> Boolean = + { data, declaration -> + when (declaration) { + // we only care about class, annotation and property + is KSClassDeclaration, + is KSAnnotation, + is KSPropertyDeclaration, + -> { + true + } + // only care about types that belong to parameters + is KSTypeReference -> data is KSPropertyDeclaration + else -> { + false + } + } + } + + private fun createRootFunction(packageName: String): FileSpec { + val rootFunction = FunSpec.builder("sanitizedToString") + .addModifiers(KModifier.INTERNAL) + .receiver(Any::class) + .returns(String::class) + .addStatement( + "return error(%S)", + "No function generated! Make sure to annotate with @Sanitize", + ) + .build() + return FileSpec.builder(packageName, "RootSanitizeFunction") + .addFunction(rootFunction) + .addFileComment("This is the root function that generated functions will overload") + .build() + } + + companion object { + const val ROOT_GENERATOR_KEY = "mrclean.rootgenerator" + const val DEBUG_KEY = "mrclean.debug" + const val PACKAGE_KEY = "mrclean.packagename" + } +} diff --git a/mr-clean-processor-core/src/main/java/com/trello/mrclean/MrCleanDataClasses.kt b/mr-clean-processor-core/src/main/java/com/trello/mrclean/MrCleanDataClasses.kt new file mode 100644 index 0000000..4faa178 --- /dev/null +++ b/mr-clean-processor-core/src/main/java/com/trello/mrclean/MrCleanDataClasses.kt @@ -0,0 +1,20 @@ +package com.trello.mrclean + +import com.google.devtools.ksp.symbol.KSFile + +data class MrCleanClassData( + val qualifiedName: String, + val simpleName: String, + val qualifiedPackage: String, + val enclosingPackage: String, + val properties: List, + val originatingFile: KSFile?, +) { + fun getFileName() = "SanitizationFor$enclosingPackage${qualifiedName.removePrefix(qualifiedPackage)}" +} + +data class MrCleanProperty( + val name: String, + val type: String? = null, + val isPublic: Boolean, +) \ No newline at end of file diff --git a/mr-clean-processor-core/src/main/java/com/trello/mrclean/MrCleanVisitor.kt b/mr-clean-processor-core/src/main/java/com/trello/mrclean/MrCleanVisitor.kt new file mode 100644 index 0000000..4e93a75 --- /dev/null +++ b/mr-clean-processor-core/src/main/java/com/trello/mrclean/MrCleanVisitor.kt @@ -0,0 +1,32 @@ +package com.trello.mrclean + +import com.google.devtools.ksp.isPublic +import com.google.devtools.ksp.processing.KSPLogger +import com.google.devtools.ksp.symbol.KSClassDeclaration +import com.google.devtools.ksp.symbol.KSPropertyDeclaration +import com.google.devtools.ksp.symbol.KSType +import com.google.devtools.ksp.symbol.KSVisitorVoid +import com.google.devtools.ksp.validate + +class MrCleanVisitor( + private val logger: KSPLogger, +) : KSVisitorVoid() { + val properties = mutableListOf() + override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) { + val properties: Sequence = classDeclaration.getAllProperties() + .filter { it.validate() } + + properties.forEach { + visitPropertyDeclaration(it, Unit) + } + } + + override fun visitPropertyDeclaration(property: KSPropertyDeclaration, data: Unit) { + val propertyName = property.simpleName.asString() + val isPublic = property.isPublic() + + val resolvedType: KSType = property.type.resolve() + val type = resolvedType.declaration.qualifiedName?.asString() + properties += MrCleanProperty(propertyName, type, isPublic) + } +} \ No newline at end of file diff --git a/mr-clean-processor-core/src/main/java/com/trello/mrclean/SanitizeGenerator.kt b/mr-clean-processor-core/src/main/java/com/trello/mrclean/SanitizeGenerator.kt new file mode 100644 index 0000000..62430ea --- /dev/null +++ b/mr-clean-processor-core/src/main/java/com/trello/mrclean/SanitizeGenerator.kt @@ -0,0 +1,45 @@ +package com.trello.mrclean + +import com.squareup.kotlinpoet.* + +internal object SanitizeGenerator { + fun generateSanitizedToString( + qualifiedClassName: String, + simpleClassName: String, + properties: List, + isDebug: Boolean, + ): FunSpec { + val debugString = properties.joinToString { + if (it.isPublic) "${it.name} = ${"$"}${it.name}" else "${it.name} = " + } + println("debug string is $debugString") + val sanitizedOutput = mapOf( + "className" to simpleClassName, +// "hexString" to Int::class.java.asTypeName(), + ) + val suppressAnnotation = AnnotationSpec.builder(Suppress::class) + .addMember("%S", "NOTHING_TO_INLINE") + .build() + val block = CodeBlock.builder() + .addNamed( + "return \"%className:L@\${hashCode().toString(16)}\"\n", + sanitizedOutput, + ) + .build() + val build = FunSpec.builder("sanitizedToString") + .addAnnotation(suppressAnnotation) + .receiver(ClassName.bestGuess(qualifiedClassName)) + .addModifiers(KModifier.INLINE, KModifier.INTERNAL) + .returns(String::class) + .apply { + if (isDebug) { + addStatement("return %P", "$simpleClassName($debugString)") + } else { + addCode(block) + } + } + .build() + println(build.toString()) + return build + } +} diff --git a/mr-clean-processor-core/src/test/java/com/trello/mrclean/SanitizeGeneratorTest.kt b/mr-clean-processor-core/src/test/java/com/trello/mrclean/SanitizeGeneratorTest.kt new file mode 100644 index 0000000..8a9562c --- /dev/null +++ b/mr-clean-processor-core/src/test/java/com/trello/mrclean/SanitizeGeneratorTest.kt @@ -0,0 +1,209 @@ +package com.trello.mrclean +import com.squareup.kotlinpoet.FileSpec +import com.squareup.kotlinpoet.FunSpec +import org.junit.Assert.* +import org.junit.Test + +class SanitizeGeneratorTest { + + data class TwoParam(val bar: Int, val meow: Int) + + @Test + fun generateDebugSanitizedToString() { + val properties = listOf( + MrCleanProperty(isPublic = true, name = "bar"), + MrCleanProperty(isPublic = true, name = "meow"), + ) + val qualifiedClassName = TwoParam::class.java.canonicalName + val simpleClassName = TwoParam::class.java.simpleName + val expectedOuput = """ + |package com.example + + |import com.trello.mrclean.SanitizeGeneratorTest + |import kotlin.String + |import kotlin.Suppress + + |@Suppress("NOTHING_TO_INLINE") + |internal inline fun SanitizeGeneratorTest.TwoParam.sanitizedToString(): String = + | ${"\""}${"\""}${"\""}TwoParam(bar = ${"$"}bar, meow = ${"$"}meow)${"\""}${"\""}${"\""} + | + """.trimMargin() + + assertEquals( + expectedOuput, + buildFile( + SanitizeGenerator.generateSanitizedToString( + qualifiedClassName, + simpleClassName, + properties, + true, + ), + ), + ) + } + + @Test + fun generateReleaseSanitizedToString() { + val properties = listOf( + MrCleanProperty(isPublic = true, name = "bar"), + MrCleanProperty(isPublic = true, name = "meow"), + ) + val qualifiedClassName = TwoParam::class.java.canonicalName + val simpleClassName = TwoParam::class.java.simpleName + val expectedOuput = """ + |package com.example + + |import com.trello.mrclean.SanitizeGeneratorTest + |import kotlin.String + |import kotlin.Suppress + + |@Suppress("NOTHING_TO_INLINE") + |internal inline fun SanitizeGeneratorTest.TwoParam.sanitizedToString(): String = + | "TwoParam@${"$"}{hashCode().toString(16)}" + | + """.trimMargin() + + assertEquals( + expectedOuput, + buildFile( + SanitizeGenerator.generateSanitizedToString( + qualifiedClassName, + simpleClassName, + properties, + false, + ), + ), + ) + } + + data class EightParam( + val one: Int, + val two: Int, + val three: Int, + val four: Int, + val five: Int, + val six: Int, + val seven: Int, + val eight: Int, + ) + + @Test + fun generateReleaseSanitizedToStringForLongClassHeader() { + val properties = listOf( + MrCleanProperty(isPublic = true, name = "one"), + MrCleanProperty(isPublic = true, name = "two"), + MrCleanProperty(isPublic = true, name = "three"), + MrCleanProperty(isPublic = true, name = "four"), + MrCleanProperty(isPublic = true, name = "five"), + MrCleanProperty(isPublic = true, name = "six"), + MrCleanProperty(isPublic = true, name = "seven"), + MrCleanProperty(isPublic = true, name = "eight"), + ) + val qualifiedClassName = EightParam::class.java.canonicalName + val simpleClassName = EightParam::class.java.simpleName + + val expectedOuput = """ + |package com.example + + |import com.trello.mrclean.SanitizeGeneratorTest + |import kotlin.String + |import kotlin.Suppress + + |@Suppress("NOTHING_TO_INLINE") + |internal inline fun SanitizeGeneratorTest.EightParam.sanitizedToString(): String = + | "EightParam@${"$"}{hashCode().toString(16)}" + | + """.trimMargin() + val output = buildFile( + SanitizeGenerator.generateSanitizedToString( + qualifiedClassName, + simpleClassName, + properties, + false, + ), + ) + + assertEquals(expectedOuput, output) + } + + @Test + fun generateDebugSanitizedToStringForLongClassHeader() { + val properties = listOf( + MrCleanProperty(isPublic = true, name = "one"), + MrCleanProperty(isPublic = true, name = "two"), + MrCleanProperty(isPublic = true, name = "three"), + MrCleanProperty(isPublic = true, name = "four"), + MrCleanProperty(isPublic = true, name = "five"), + MrCleanProperty(isPublic = true, name = "six"), + MrCleanProperty(isPublic = true, name = "seven"), + MrCleanProperty(isPublic = true, name = "eight"), + ) + val qualifiedClassName = EightParam::class.java.canonicalName + val simpleClassName = EightParam::class.java.simpleName + + val expectedOuput = """ + |package com.example + + |import com.trello.mrclean.SanitizeGeneratorTest + |import kotlin.String + |import kotlin.Suppress + + |@Suppress("NOTHING_TO_INLINE") + |internal inline fun SanitizeGeneratorTest.EightParam.sanitizedToString(): String = + | ${"\""}${"\""}${"\""}EightParam(one = ${"$"}one, two = ${"$"}two, three = ${"$"}three, four = ${"$"}four, five = ${"$"}five, six = ${"$"}six, seven = ${"$"}seven, eight = ${"$"}eight)${"\""}${"\""}${"\""} + | + """.trimMargin() + val output = buildFile( + SanitizeGenerator.generateSanitizedToString( + qualifiedClassName, + simpleClassName, + properties, + true, + ), + ) + + assertEquals(expectedOuput, output) + } + + private fun buildFile(funSpec: FunSpec): String { + val output = StringBuilder() + FileSpec.builder("com.example", "Sanitizations") + .addFunction(funSpec) + .build() + .writeTo(output) + return output.toString() + } + + @Test + fun generateDebugSanitizedToStringIgnoresPrivateProperties() { + val properties = listOf( + MrCleanProperty(isPublic = false, name = "bar"), + MrCleanProperty(isPublic = true, name = "meow"), + ) + val qualifiedClassName = TwoParam::class.java.canonicalName + val simpleClassName = TwoParam::class.java.simpleName + val expectedOuput = """ + |package com.example + + |import com.trello.mrclean.SanitizeGeneratorTest + |import kotlin.String + |import kotlin.Suppress + + |@Suppress("NOTHING_TO_INLINE") + |internal inline fun SanitizeGeneratorTest.TwoParam.sanitizedToString(): String = + | ${"\""}${"\""}${"\""}TwoParam(bar = , meow = ${"$"}meow)${"\""}${"\""}${"\""} + | + """.trimMargin() + assertEquals( + expectedOuput, + buildFile( + SanitizeGenerator.generateSanitizedToString( + qualifiedClassName, + simpleClassName, + properties, + true, + ), + ), + ) + } +} \ No newline at end of file diff --git a/mr-clean-processor/build.gradle b/mr-clean-processor/build.gradle deleted file mode 100644 index fd25118..0000000 --- a/mr-clean-processor/build.gradle +++ /dev/null @@ -1,24 +0,0 @@ -plugins { - alias libs.plugins.kotlin.jvm - alias libs.plugins.kotlin.kapt - alias libs.plugins.vanniktech.publish -} -dependencies { - compileOnly project(':mr-clean-annotations') - compileOnly libs.autoService - kapt libs.autoService - - api libs.incap.runtime - compileOnly libs.incap.processor - - implementation libs.kotlin.stdlib - - implementation libs.kotlinx.metadataJvm - - implementation libs.kotlinpoet - - testImplementation libs.junit -} -sourceCompatibility = JavaVersion.VERSION_17 -targetCompatibility = JavaVersion.VERSION_17 - diff --git a/mr-clean-processor/build.gradle.kts b/mr-clean-processor/build.gradle.kts new file mode 100644 index 0000000..e9fd389 --- /dev/null +++ b/mr-clean-processor/build.gradle.kts @@ -0,0 +1,26 @@ +plugins { + alias(libs.plugins.kotlin.jvm) + alias(libs.plugins.ksp) + alias(libs.plugins.vanniktech.publish) +} + +dependencies { + implementation(project(":mr-clean-processor-core")) + implementation(project(":mr-clean-runtime")) + ksp(libs.autoservice.ksp) + implementation(libs.autoservice.annotations) + + implementation(libs.kotlin.stdlib) + + implementation(libs.kotlinPoet.core) + implementation(libs.kotlinPoet.ksp) + + implementation(libs.ksp) + + testImplementation(libs.junit) +} + +ksp { + arg("autoserviceKsp.verify", "true") + arg("autoserviceKsp.verbose", "true") +} diff --git a/mr-clean-processor/src/main/java/com/trello/mrclean/KotlinTypes.kt b/mr-clean-processor/src/main/java/com/trello/mrclean/KotlinTypes.kt deleted file mode 100644 index 01eb90e..0000000 --- a/mr-clean-processor/src/main/java/com/trello/mrclean/KotlinTypes.kt +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2018 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.trello.mrclean - -import com.squareup.kotlinpoet.ClassName -import com.squareup.kotlinpoet.ParameterizedTypeName -import com.squareup.kotlinpoet.TypeName - -internal fun TypeName.rawType(): ClassName { - return when (this) { - is ClassName -> this - is ParameterizedTypeName -> rawType - else -> throw IllegalArgumentException("Cannot get raw type from $this") - } -} - -internal fun TypeName.asNullableIf(condition: Boolean): TypeName { - return if (condition) copy(nullable = true) else this -} \ No newline at end of file diff --git a/mr-clean-processor/src/main/java/com/trello/mrclean/Metadata.kt b/mr-clean-processor/src/main/java/com/trello/mrclean/Metadata.kt deleted file mode 100644 index 0d0b699..0000000 --- a/mr-clean-processor/src/main/java/com/trello/mrclean/Metadata.kt +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright (C) 2018 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.trello.mrclean - -import com.squareup.kotlinpoet.ANY -import com.squareup.kotlinpoet.ClassName -import com.squareup.kotlinpoet.KModifier -import com.squareup.kotlinpoet.TypeName -import com.squareup.kotlinpoet.TypeVariableName -import com.squareup.kotlinpoet.WildcardTypeName -import kotlinx.metadata.Flag -import kotlinx.metadata.Flags -import kotlinx.metadata.KmClassVisitor -import kotlinx.metadata.KmPropertyVisitor -import kotlinx.metadata.KmTypeVisitor -import kotlinx.metadata.KmVariance -import kotlinx.metadata.jvm.KotlinClassHeader -import kotlinx.metadata.jvm.KotlinClassMetadata -import javax.lang.model.element.Element -import javax.lang.model.element.ExecutableElement - -@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") -internal fun Element.getClassHeader(): KotlinClassHeader? { - return getAnnotation(Metadata::class.java)?.run { - KotlinClassHeader(kind, metadataVersion, data1, data2, extraString, packageName, extraInt) - } -} - -internal fun KotlinClassHeader.readKotlinClassMetadata(): KotlinClassMetadata? { - return KotlinClassMetadata.read(this) -} - -internal fun KmVariance.asKModifier(): KModifier? { - return when (this) { - KmVariance.IN -> KModifier.IN - KmVariance.OUT -> KModifier.OUT - KmVariance.INVARIANT -> null - } -} - -/** - * Resolves the TypeName of this type as it would be seen in the source code, including nullability - * and generic type parameters. - * - * @param flags the [Flags] associated with this type - * @param [getTypeParameter] a function that returns the type parameter for the given index. **Only - * called if [TypeNameKmTypeVisitor.visitTypeParameter] is called** - * @param useTypeAlias indicates whether or not to use type aliases or resolve their underlying - * types - */ -internal class TypeNameKmTypeVisitor(flags: Flags, - private val getTypeParameter: ((index: Int) -> TypeName)? = null, - private val useTypeAlias: Boolean = true, - private val receiver: (TypeName) -> Unit) : KmTypeVisitor() { - - private val nullable = false - //Flag.Type.IS_NULLABLE(flags) - private var className: String? = null - private var typeAliasName: String? = null - private var typeAliasType: TypeName? = null - private var flexibleTypeUpperBound: TypeName? = null - private var outerType: TypeName? = null - private var typeParameter: TypeName? = null - private val argumentList = mutableListOf() - - override fun visitAbbreviatedType(flags: Flags): KmTypeVisitor? { - if (!useTypeAlias) { - return null - } - return TypeNameKmTypeVisitor(flags, getTypeParameter, useTypeAlias) { - typeAliasType = it - } - } - - override fun visitArgument(flags: Flags, variance: KmVariance): KmTypeVisitor? { - return TypeNameKmTypeVisitor(flags, getTypeParameter, useTypeAlias) { - argumentList.add( - when (variance) { - KmVariance.IN -> WildcardTypeName.consumerOf(it) - KmVariance.OUT -> WildcardTypeName.producerOf(it) - KmVariance.INVARIANT -> it - } - ) - } - } - - override fun visitClass(name: kotlinx.metadata.ClassName) { - className = name - } - - override fun visitFlexibleTypeUpperBound(flags: Flags, - typeFlexibilityId: String?): KmTypeVisitor? { - return TypeNameKmTypeVisitor(flags, getTypeParameter, useTypeAlias) { - flexibleTypeUpperBound = WildcardTypeName.producerOf(it) - } - } - - override fun visitOuterType(flags: Flags): KmTypeVisitor? { - return TypeNameKmTypeVisitor(flags, getTypeParameter, useTypeAlias) { - outerType = WildcardTypeName.consumerOf(it) - } - } - - override fun visitStarProjection() { - argumentList.add(WildcardTypeName.producerOf(ANY)) - } - - override fun visitTypeAlias(name: kotlinx.metadata.ClassName) { - if (!useTypeAlias) { - return - } - typeAliasName = name - } - - override fun visitTypeParameter(id: Int) { - typeParameter = getTypeParameter?.invoke(id) ?: throw IllegalStateException( - "Visiting TypeParameter when there are no type parameters!") - } - - override fun visitEnd() { - var finalType = flexibleTypeUpperBound ?: outerType ?: typeParameter - if (finalType == null) { - if (useTypeAlias) { - finalType = typeAliasName?.let { ClassName.bestGuess(it.replace("/", ".")) } - } - if (finalType == null) { - finalType = typeAliasType ?: className?.let { ClassName.bestGuess(it.replace("/", ".")) } - ?: throw IllegalStateException("No valid typename found!") - } - } - - finalType = finalType.asNullableIf(nullable) - receiver(finalType) - } -} - -internal fun KotlinClassMetadata.Class.readClassData(): ClassData { - lateinit var className: String - val typeParameters = LinkedHashMap() - val typeParamResolver = { id: Int -> typeParameters[id]!! } - val properties = mutableListOf() - accept(object : KmClassVisitor() { - override fun visit(flags: Flags, name: kotlinx.metadata.ClassName) { - super.visit(flags, name) - className = name - } - - override fun visitProperty(flags: Flags, - name: String, - getterFlags: Flags, - setterFlags: Flags): KmPropertyVisitor? { - return object : KmPropertyVisitor() { - lateinit var type: TypeName - override fun visitEnd() { - properties += PropertyData(flags, name, type) - } - - override fun visitReturnType(flags: Flags): KmTypeVisitor? { - return TypeNameKmTypeVisitor(flags, typeParamResolver) { - type = it - } - } - } - } - }) - - return ClassData(className.replace("/", "."), - properties) -} - -internal data class ClassData( - val name: String, - val properties: List -) { - fun getPropertyOrNull(methodElement: ExecutableElement): PropertyData? { - return methodElement.simpleName.toString() - .takeIf { it.endsWith(kotlinPropertyAnnotationsFunPostfix) } - ?.substringBefore(kotlinPropertyAnnotationsFunPostfix) - ?.let { propertyName -> properties.firstOrNull { propertyName == it.name } } - } - - val className: String = name.takeLastWhile { it != '.' } - - companion object { - /** - * Postfix of the method name containing the [kotlin.Metadata] annotation for the relative property. - * @see [getPropertyOrNull] - */ - const val kotlinPropertyAnnotationsFunPostfix = "\$annotations" - } -} - -internal data class ConstructorData( - val flags: Flags, - val parameters: List -) - -internal data class ParameterData( - val flags: Flags, - val name: String, - val type: TypeName, - val isVarArg: Boolean = false -) - -internal data class PropertyData( - val flags: Flags, - val name: String, - val type: TypeName, -) { - fun isPublic() = Flag.IS_PUBLIC.invoke(flags) -} diff --git a/mr-clean-processor/src/main/java/com/trello/mrclean/MrCleanProcessor.kt b/mr-clean-processor/src/main/java/com/trello/mrclean/MrCleanProcessor.kt index b4f9076..2e51ff5 100644 --- a/mr-clean-processor/src/main/java/com/trello/mrclean/MrCleanProcessor.kt +++ b/mr-clean-processor/src/main/java/com/trello/mrclean/MrCleanProcessor.kt @@ -1,148 +1,18 @@ -/* - * Copyright @ 2018 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.trello.mrclean -import com.google.auto.service.AutoService -import com.squareup.kotlinpoet.FileSpec -import com.trello.mrclean.annotations.Sanitize -import kotlinx.metadata.internal.extensions.MetadataExtensions -import kotlinx.metadata.jvm.KotlinClassMetadata -import net.ltgt.gradle.incap.IncrementalAnnotationProcessor -import net.ltgt.gradle.incap.IncrementalAnnotationProcessorType -import java.io.File -import javax.annotation.processing.AbstractProcessor -import javax.annotation.processing.Filer -import javax.annotation.processing.Messager -import javax.annotation.processing.ProcessingEnvironment -import javax.annotation.processing.Processor -import javax.annotation.processing.RoundEnvironment -import javax.lang.model.SourceVersion -import javax.lang.model.element.TypeElement -import javax.lang.model.util.Elements -import javax.lang.model.util.Types -import javax.tools.Diagnostic - -@AutoService(Processor::class) -@IncrementalAnnotationProcessor(IncrementalAnnotationProcessorType.ISOLATING) -class MrCleanProcessor : AbstractProcessor() { - - private lateinit var messager: Messager - private lateinit var elementUtils: Elements - private lateinit var typeUtils: Types - private lateinit var filer: Filer - private var generatedDir: File? = null - private var isDebug: Boolean = false - private var packageName: String? = null - - private val sanitize = Sanitize::class.java - - override fun getSupportedAnnotationTypes(): MutableSet = mutableSetOf( - sanitize.canonicalName - ) - - override fun getSupportedSourceVersion(): SourceVersion = SourceVersion.latest() - - override fun getSupportedOptions() = mutableSetOf( - OPTION_KAPT_GENERATED, - OPTION_DEBUG, - OPTION_PACKAGE_NAME - ) - - override fun init(processingEnv: ProcessingEnvironment) { - super.init(processingEnv) - messager = processingEnv.messager - elementUtils = processingEnv.elementUtils - typeUtils = processingEnv.typeUtils - filer = processingEnv.filer - generatedDir = processingEnv.options[OPTION_KAPT_GENERATED]?.let(::File) - // load properties applied by MrCleanPlugin - isDebug = processingEnv.options[OPTION_DEBUG] == "true" - packageName = processingEnv.options[OPTION_PACKAGE_NAME] - } - - override fun process(annotations: MutableSet, roundEnv: RoundEnvironment): Boolean { - val funs = roundEnv.getElementsAnnotatedWith(sanitize) - .map { - val classHeader = it.getClassHeader()!! - val metadata = classHeader.readKotlinClassMetadata() - it to when (metadata) { - is KotlinClassMetadata.Class -> metadata.readClassData() - else -> error("not a class") - } - } - .map { (element, classData) -> - element to SanitizeGenerator.generateSanitizedToString(classData, isDebug) - .toBuilder() - .addOriginatingElement(element) - .build() - } - - if (funs.isNotEmpty() && packageName == null) { - messager.printMessage(Diagnostic.Kind.ERROR, "MrClean: PackageName is not available at processing time. You may be trying to annotate test classes, which is unsupported.") - return true +import com.google.devtools.ksp.processing.* +import com.google.devtools.ksp.symbol.* + +class MrCleanProcessor( + val codeGenerator: CodeGenerator, + val options: Map, + private val logger: KSPLogger, +) : SymbolProcessor { + override fun process(resolver: Resolver): List { + return MrCleanCoreProcessor( + codeGenerator = codeGenerator, + options = options, + logger = logger, + ).process(resolver) } - - funs.map { (element, funSpec) -> - val enclosingElementName = element.enclosingElement.simpleName.toString().capitalize() - FileSpec.builder(packageName!!, "SanitizationFor$enclosingElementName${element.simpleName}") - .apply { - if (isDebug) addFileComment("Debug") else addFileComment("Release") - } - .addFunction(funSpec) - .build() - } - .forEach { it.writeTo(processingEnv.filer) } - - return true - } - - - companion object { - /** - * Name of the processor option containing the path to the Kotlin generated src dir. - */ - private const val OPTION_KAPT_GENERATED = "kapt.kotlin.generated" - - /** - * Compiler options that get added by MrCleanPlugin - * mrclean.debug - whether the variant's build type is debuggable - * mrclean.packagename - the root package name for the project - * - * Changes here must be reflected in MrCleanPlugin.kt - */ - private const val OPTION_DEBUG = "mrclean.debug" - private const val OPTION_PACKAGE_NAME = "mrclean.packagename" - - init { - // https://youtrack.jetbrains.net/issue/KT-24881 - with(Thread.currentThread()) { - val classLoader = contextClassLoader - contextClassLoader = MetadataExtensions::class.java.classLoader - try { - MetadataExtensions.INSTANCES - } finally { - contextClassLoader = classLoader - } - } - } - } -} - -fun Messager.note(message: String) { - printMessage(Diagnostic.Kind.NOTE, message) -} - +} \ No newline at end of file diff --git a/mr-clean-processor/src/main/java/com/trello/mrclean/MrCleanProcessorProvider.kt b/mr-clean-processor/src/main/java/com/trello/mrclean/MrCleanProcessorProvider.kt new file mode 100644 index 0000000..ac26ebf --- /dev/null +++ b/mr-clean-processor/src/main/java/com/trello/mrclean/MrCleanProcessorProvider.kt @@ -0,0 +1,16 @@ +package com.trello.mrclean + +import com.google.auto.service.AutoService +import com.google.devtools.ksp.processing.SymbolProcessor +import com.google.devtools.ksp.processing.SymbolProcessorEnvironment +import com.google.devtools.ksp.processing.SymbolProcessorProvider + +@AutoService(SymbolProcessorProvider::class) +class MrCleanProcessorProvider : SymbolProcessorProvider { + override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor = + MrCleanProcessor( + codeGenerator = environment.codeGenerator, + options = environment.options, + logger = environment.logger, + ) +} diff --git a/mr-clean-processor/src/main/java/com/trello/mrclean/SanitizeGenerator.kt b/mr-clean-processor/src/main/java/com/trello/mrclean/SanitizeGenerator.kt deleted file mode 100644 index b784fde..0000000 --- a/mr-clean-processor/src/main/java/com/trello/mrclean/SanitizeGenerator.kt +++ /dev/null @@ -1,40 +0,0 @@ -package com.trello.mrclean - -import com.squareup.kotlinpoet.AnnotationSpec -import com.squareup.kotlinpoet.ClassName -import com.squareup.kotlinpoet.CodeBlock -import com.squareup.kotlinpoet.FunSpec -import com.squareup.kotlinpoet.KModifier -import com.squareup.kotlinpoet.asTypeName - -internal object SanitizeGenerator { - fun generateSanitizedToString(classData: ClassData, isDebug: Boolean): FunSpec { - val debugString = classData.properties.joinToString { - if (it.isPublic()) "${it.name} = ${"$"}${it.name}" else "${it.name} = " - } - val sanitizedOutput = mapOf( - "className" to classData.className, - "hexString" to Integer::class.java.asTypeName() - ) - val suppressAnnotation = AnnotationSpec.builder(Suppress::class) - .addMember("%S", "NOTHING_TO_INLINE") - .build() - val block = CodeBlock.builder() - .addNamed("return \"%className:L@\${%hexString:T.toHexString(hashCode())}\"\n", sanitizedOutput) - .build() - return FunSpec.builder("sanitizedToString") - .addAnnotation(suppressAnnotation) - .receiver(ClassName.bestGuess(classData.name)) - .addModifiers(KModifier.INLINE, KModifier.INTERNAL) - .returns(String::class) - .apply { - if (isDebug) { - addStatement("return %P", "${classData.className}($debugString)") - } - else { - addCode(block) - } - } - .build() - } -} diff --git a/mr-clean-processor/src/test/java/com/trello/mrclean/SanitizeGeneratorTest.kt b/mr-clean-processor/src/test/java/com/trello/mrclean/SanitizeGeneratorTest.kt deleted file mode 100644 index 052311f..0000000 --- a/mr-clean-processor/src/test/java/com/trello/mrclean/SanitizeGeneratorTest.kt +++ /dev/null @@ -1,171 +0,0 @@ -package com.trello.mrclean - -import com.squareup.kotlinpoet.FileSpec -import com.squareup.kotlinpoet.FunSpec -import com.squareup.kotlinpoet.INT -import com.squareup.kotlinpoet.TypeName -import com.squareup.kotlinpoet.asTypeName -import kotlinx.metadata.Flag -import kotlinx.metadata.flagsOf -import org.junit.Test - -import org.junit.Assert.* - -class SanitizeGeneratorTest { - - data class TwoParam(val bar: Int, val meow: Int) - - @Test - fun generateDebugSanitizedToString() { - val propertyData = listOf( - PropertyData(flagsOf(Flag.IS_PUBLIC), "bar", INT), - PropertyData(flagsOf(Flag.IS_PUBLIC), "meow", INT) - ) - val classData = ClassData(TwoParam::class.java.canonicalName, propertyData) - val expectedOuput = """ - |package com.example - - |import com.trello.mrclean.SanitizeGeneratorTest - |import kotlin.String - |import kotlin.Suppress - - |@Suppress("NOTHING_TO_INLINE") - |internal inline fun SanitizeGeneratorTest.TwoParam.sanitizedToString(): String = - | ${"\""}${"\""}${"\""}TwoParam(bar = ${"$"}bar, meow = ${"$"}meow)${"\""}${"\""}${"\""} - | - """.trimMargin() - - assertEquals(expectedOuput, buildFile(SanitizeGenerator.generateSanitizedToString(classData, true))) - - } - - @Test - fun generateReleaseSanitizedToString() { - val propertyData = listOf( - PropertyData(flagsOf(Flag.IS_PUBLIC), "bar", INT), - PropertyData(flagsOf(Flag.IS_PUBLIC), "meow", INT) - ) - val classData = ClassData(TwoParam::class.java.canonicalName, propertyData) - val expectedOuput = """ - |package com.example - - |import com.trello.mrclean.SanitizeGeneratorTest - |import java.lang.Integer - |import kotlin.String - |import kotlin.Suppress - - |@Suppress("NOTHING_TO_INLINE") - |internal inline fun SanitizeGeneratorTest.TwoParam.sanitizedToString(): String = - | "TwoParam@${"$"}{Integer.toHexString(hashCode())}" - | - """.trimMargin() - - assertEquals(expectedOuput, buildFile(SanitizeGenerator.generateSanitizedToString(classData, false))) - - } - - data class EightParam( - val one: Int, - val two: Int, - val three: Int, - val four: Int, - val five: Int, - val six: Int, - val seven: Int, - val eight: Int - ) - - @Test - fun generateReleaseSanitizedToStringForLongClassHeader() { - val propertyData = listOf( - PropertyData(flagsOf(Flag.IS_PUBLIC), "one", INT), - PropertyData(flagsOf(Flag.IS_PUBLIC), "two", INT), - PropertyData(flagsOf(Flag.IS_PUBLIC), "three", INT), - PropertyData(flagsOf(Flag.IS_PUBLIC), "four", INT), - PropertyData(flagsOf(Flag.IS_PUBLIC), "five", INT), - PropertyData(flagsOf(Flag.IS_PUBLIC), "six", INT), - PropertyData(flagsOf(Flag.IS_PUBLIC), "seven", INT), - PropertyData(flagsOf(Flag.IS_PUBLIC), "eight", INT) - ) - - val classData = ClassData(EightParam::class.java.canonicalName, propertyData) - val expectedOuput = """ - |package com.example - - |import com.trello.mrclean.SanitizeGeneratorTest - |import java.lang.Integer - |import kotlin.String - |import kotlin.Suppress - - |@Suppress("NOTHING_TO_INLINE") - |internal inline fun SanitizeGeneratorTest.EightParam.sanitizedToString(): String = - | "EightParam@${"$"}{Integer.toHexString(hashCode())}" - | - """.trimMargin() - val output = buildFile(SanitizeGenerator.generateSanitizedToString(classData, false)) - - assertEquals(expectedOuput, output) - } - - @Test - fun generateDebugSanitizedToStringForLongClassHeader() { - val propertyData = listOf( - PropertyData(flagsOf(Flag.IS_PUBLIC), "one", INT), - PropertyData(flagsOf(Flag.IS_PUBLIC), "two", INT), - PropertyData(flagsOf(Flag.IS_PUBLIC), "three", INT), - PropertyData(flagsOf(Flag.IS_PUBLIC), "four", INT), - PropertyData(flagsOf(Flag.IS_PUBLIC), "five", INT), - PropertyData(flagsOf(Flag.IS_PUBLIC), "six", INT), - PropertyData(flagsOf(Flag.IS_PUBLIC), "seven", INT), - PropertyData(flagsOf(Flag.IS_PUBLIC), "eight", INT) - ) - - val classData = ClassData(EightParam::class.java.canonicalName, propertyData) - val expectedOuput = """ - |package com.example - - |import com.trello.mrclean.SanitizeGeneratorTest - |import kotlin.String - |import kotlin.Suppress - - |@Suppress("NOTHING_TO_INLINE") - |internal inline fun SanitizeGeneratorTest.EightParam.sanitizedToString(): String = - | ${"\""}${"\""}${"\""}EightParam(one = ${"$"}one, two = ${"$"}two, three = ${"$"}three, four = ${"$"}four, five = ${"$"}five, six = ${"$"}six, seven = ${"$"}seven, eight = ${"$"}eight)${"\""}${"\""}${"\""} - | - """.trimMargin() - val output = buildFile(SanitizeGenerator.generateSanitizedToString(classData, true)) - - assertEquals(expectedOuput, output) - } - - private fun buildFile(funSpec: FunSpec): String { - val output = StringBuilder() - FileSpec.builder("com.example", "Sanitizations") - .addFunction(funSpec) - .build() - .writeTo(output) - return output.toString() - } - - @Test - fun generateDebugSanitizedToStringIgnoresPrivateProperties() { - val propertyData = listOf( - PropertyData(flagsOf(Flag.IS_PRIVATE), "bar", INT), - PropertyData(flagsOf(Flag.IS_PUBLIC), "meow", INT) - ) - val classData = ClassData(TwoParam::class.java.canonicalName, propertyData) - val expectedOuput = """ - |package com.example - - |import com.trello.mrclean.SanitizeGeneratorTest - |import kotlin.String - |import kotlin.Suppress - - |@Suppress("NOTHING_TO_INLINE") - |internal inline fun SanitizeGeneratorTest.TwoParam.sanitizedToString(): String = - | ${"\""}${"\""}${"\""}TwoParam(bar = , meow = ${"$"}meow)${"\""}${"\""}${"\""} - | - """.trimMargin() - assertEquals(expectedOuput, buildFile(SanitizeGenerator.generateSanitizedToString(classData, true))) - } -} \ No newline at end of file diff --git a/mr-clean-annotations/.gitignore b/mr-clean-runtime/.gitignore similarity index 100% rename from mr-clean-annotations/.gitignore rename to mr-clean-runtime/.gitignore diff --git a/mr-clean-annotations/build.gradle b/mr-clean-runtime/build.gradle similarity index 100% rename from mr-clean-annotations/build.gradle rename to mr-clean-runtime/build.gradle diff --git a/mr-clean-runtime/gradle.properties b/mr-clean-runtime/gradle.properties new file mode 100644 index 0000000..5f501f4 --- /dev/null +++ b/mr-clean-runtime/gradle.properties @@ -0,0 +1,3 @@ +POM_NAME=Mr. Clean Runtime +POM_ARTIFACT_ID=mr-clean-runtime +POM_PACKAGING=jar \ No newline at end of file diff --git a/mr-clean-annotations/src/main/java/com/trello/mrclean/annotations/Sanitize.kt b/mr-clean-runtime/src/main/java/com/trello/mrclean/annotations/Sanitize.kt similarity index 96% rename from mr-clean-annotations/src/main/java/com/trello/mrclean/annotations/Sanitize.kt rename to mr-clean-runtime/src/main/java/com/trello/mrclean/annotations/Sanitize.kt index e4d451a..2bb6f88 100644 --- a/mr-clean-annotations/src/main/java/com/trello/mrclean/annotations/Sanitize.kt +++ b/mr-clean-runtime/src/main/java/com/trello/mrclean/annotations/Sanitize.kt @@ -28,4 +28,5 @@ package com.trello.mrclean.annotations * ``` */ @Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) annotation class Sanitize diff --git a/settings.gradle b/settings.gradle index 63bd050..17abd8c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1,5 @@ -include ':mr-clean-annotations' +include ':mr-clean-runtime' include ':mr-clean-processor' -include ':mr-clean-plugin' +include ':mr-clean-processor-core' +include ':mr-clean-debug-processor' +include ':mr-clean-plugin' \ No newline at end of file From 8533238b57a32cc7e27e8a28a186185cff29beaa Mon Sep 17 00:00:00 2001 From: Brian Parent Date: Fri, 10 Nov 2023 08:59:20 -0600 Subject: [PATCH 3/7] Updated readme --- CHANGELOG.md | 7 +++++++ README.md | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9f5694..adf744f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# 2.0.0 +* Breaking revision - KSP support and support for KAPT has been dropped +* 1.2.2 should still be used for users using KAPT as only KSP will be supported moving forward +* mr-clean-annotations has been renamed mr-clean-runtime +* mr-clean-debug-processor is used to generate the debug implementation of the root function +* mr-clean-processor will generate the sanitized version of strings + # 1.2.2 * Updates Kotlin to 1.9.0 * Updates Gradle to 8.1.0 diff --git a/README.md b/README.md index e11ce17..785fbfe 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,55 @@ inline fun SensitiveData.sanitizedToString(condition: Boolean): String = "Sensit Don't leak sensitive info ever again, trust in Mr. Clean. # Usage +```groovy +buildscript { + repositories { + mavenCentral() + google() + } + dependencies { + classpath 'com.trello.mrclean:mr-clean-plugin:2.0.0' + } +} +``` + +and then apply to your modules +```groovy +apply plugin: 'com.trello.mrclean' + +``` + +# Don't care for plugins? +The Mr. Clean plugin adds `mr-clean-debug-processor` to `debug` BuildTypes automatically and `mr-clean-processor` to all others as well as setting the `packageName` defined in your manifest. Finally it generates the root `Any?.sanitizeToString` function for you (so you don't have errors pre compile) + +If you'd prefer to manually control this, you can. + +```groovy +ksp { + // Anything set here will override *ALL* values across all + // Generates root function Any.sanitizeToString + arg("mrclean.rootgenerator", "true") + // Package name to generate in under generated + arg("mrclean.packagename", "true") + // Setting this will override d + arg("mrclean.debug", "true") +} + +dependencies { + implmentation 'com.trello.mrclean:mr-clean-runtime:2.0.0' + // Debug by default has KSP arg "mrclean.debug" set to false, "mrclean.rootgenerator" to false + kspRelease 'com.trello.mrclean:mr-clean-processor:2.0.0' + // Debug by default has KSP arg "mrclean.debug" set to true, "mrclean.rootgenerator" to false + kspDebug 'com.trello.mrclean:mr-clean-debug-processor:2.0.0' +} +``` + + +# Looking for KAPT support? +Use 1.2.2 as Mr. Clean is pretty stable but we are only supporting KSP moving forward + +1.2.2 usage is + ```groovy buildscript { repositories { From 439e7934e8f95964fffaef8aa982b36bca33f69b Mon Sep 17 00:00:00 2001 From: Brian Parent Date: Fri, 10 Nov 2023 12:21:25 -0600 Subject: [PATCH 4/7] Added tests to generation --- gradle/libs.versions.toml | 18 +- .../trello/mrclean/MrCleanCoreProcessor.kt | 15 +- .../com/trello/mrclean/SanitizeGenerator.kt | 9 +- mr-clean-processor/build.gradle.kts | 3 + .../trello/mrclean/MrCleanProcessorTest.kt | 157 ++++++++++++++++++ .../mrclean/TestMrCleanProcessorProvider.kt | 14 ++ 6 files changed, 196 insertions(+), 20 deletions(-) create mode 100644 mr-clean-processor/src/test/java/com/trello/mrclean/MrCleanProcessorTest.kt create mode 100644 mr-clean-processor/src/test/java/com/trello/mrclean/TestMrCleanProcessorProvider.kt diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e096617..43f3f35 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,10 +9,7 @@ kotlinPoet = "1.14.2" agp = "8.1.2" org-jetbrains-kotlin-android = "1.9.10" junit = "4.13.2" -runner = "1.0.2" -espresso-core = "3.0.2" -appcompat-v7 = "28.0.0" -compose-bom = "2023.10.01" +compileTesting = "1.5.0" [libraries] autoservice-ksp = { module = "dev.zacsweers.autoservice:auto-service-ksp", version.ref = "autoserviceKsp" } @@ -26,14 +23,13 @@ kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk7", version.re ksp = { module = "com.google.devtools.ksp:symbol-processing-api ", version.ref = "ksp" } plugin-ksp = { module = "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" } junit-junit = { group = "junit", name = "junit", version.ref = "junit" } -runner = { group = "com.android.support.test", name = "runner", version.ref = "runner" } -espresso-core = { group = "com.android.support.test.espresso", name = "espresso-core", version.ref = "espresso-core" } -appcompat-v7 = { group = "com.android.support", name = "appcompat-v7", version.ref = "appcompat-v7" } +truth = { module ="com.google.truth:truth", version = "1.1.4" } + +compileTesting-core = { module = "com.github.tschuchortdev:kotlin-compile-testing", version.ref = "compileTesting" } +compileTesting-ksp = { module = "com.github.tschuchortdev:kotlin-compile-testing-ksp", version.ref = "compileTesting" } +zsweers-compileTesting-core = { module = "dev.zacsweers.kctfork:core", version = "0.2.1"} +zsweers-compileTesting-ksp= { module = "dev.zacsweers.kctfork:ksp", version = "0.2.1"} -compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" } -ui = { group = "androidx.compose.ui", name = "ui" } -material3 = { group = "androidx.compose.material3", name = "material3" } -activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activity-compose" } [plugins] versions = { id = "com.github.ben-manes.versions", version.ref = "versionsPlugin" } diff --git a/mr-clean-processor-core/src/main/java/com/trello/mrclean/MrCleanCoreProcessor.kt b/mr-clean-processor-core/src/main/java/com/trello/mrclean/MrCleanCoreProcessor.kt index acfb515..0d1ba84 100644 --- a/mr-clean-processor-core/src/main/java/com/trello/mrclean/MrCleanCoreProcessor.kt +++ b/mr-clean-processor-core/src/main/java/com/trello/mrclean/MrCleanCoreProcessor.kt @@ -3,7 +3,11 @@ package com.trello.mrclean import com.google.devtools.ksp.processing.CodeGenerator import com.google.devtools.ksp.processing.KSPLogger import com.google.devtools.ksp.processing.Resolver -import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.symbol.KSAnnotated +import com.google.devtools.ksp.symbol.KSAnnotation +import com.google.devtools.ksp.symbol.KSClassDeclaration +import com.google.devtools.ksp.symbol.KSNode +import com.google.devtools.ksp.symbol.KSPropertyDeclaration import com.google.devtools.ksp.validate import com.squareup.kotlinpoet.FileSpec import com.squareup.kotlinpoet.FunSpec @@ -27,8 +31,10 @@ class MrCleanCoreProcessor( } val annotationName = Sanitize::class.qualifiedName!! val unfilteredSymbols = resolver.getSymbolsWithAnnotation(annotationName) - val symbolsNotProcessed = unfilteredSymbols.filterNot { it.validate(symbolPredicate) }.toList() - val symbols = unfilteredSymbols.filterIsInstance().filter { it.validate(symbolPredicate) } + val symbolsNotProcessed = + unfilteredSymbols.filterNot { it.validate(symbolPredicate) }.toList() + val symbols = unfilteredSymbols.filterIsInstance() + .filter { it.validate(symbolPredicate) } if (!symbols.iterator().hasNext()) { logger.info("Mr. Clean found no symbols to process, exiting") @@ -113,8 +119,7 @@ class MrCleanCoreProcessor( -> { true } - // only care about types that belong to parameters - is KSTypeReference -> data is KSPropertyDeclaration + else -> { false } diff --git a/mr-clean-processor-core/src/main/java/com/trello/mrclean/SanitizeGenerator.kt b/mr-clean-processor-core/src/main/java/com/trello/mrclean/SanitizeGenerator.kt index 62430ea..ff24936 100644 --- a/mr-clean-processor-core/src/main/java/com/trello/mrclean/SanitizeGenerator.kt +++ b/mr-clean-processor-core/src/main/java/com/trello/mrclean/SanitizeGenerator.kt @@ -1,6 +1,10 @@ package com.trello.mrclean -import com.squareup.kotlinpoet.* +import com.squareup.kotlinpoet.AnnotationSpec +import com.squareup.kotlinpoet.ClassName +import com.squareup.kotlinpoet.CodeBlock +import com.squareup.kotlinpoet.FunSpec +import com.squareup.kotlinpoet.KModifier internal object SanitizeGenerator { fun generateSanitizedToString( @@ -12,10 +16,8 @@ internal object SanitizeGenerator { val debugString = properties.joinToString { if (it.isPublic) "${it.name} = ${"$"}${it.name}" else "${it.name} = " } - println("debug string is $debugString") val sanitizedOutput = mapOf( "className" to simpleClassName, -// "hexString" to Int::class.java.asTypeName(), ) val suppressAnnotation = AnnotationSpec.builder(Suppress::class) .addMember("%S", "NOTHING_TO_INLINE") @@ -39,7 +41,6 @@ internal object SanitizeGenerator { } } .build() - println(build.toString()) return build } } diff --git a/mr-clean-processor/build.gradle.kts b/mr-clean-processor/build.gradle.kts index e9fd389..0441262 100644 --- a/mr-clean-processor/build.gradle.kts +++ b/mr-clean-processor/build.gradle.kts @@ -18,6 +18,9 @@ dependencies { implementation(libs.ksp) testImplementation(libs.junit) + testImplementation(libs.truth) + testImplementation(libs.zsweers.compileTesting.core) + testImplementation(libs.zsweers.compileTesting.ksp) } ksp { diff --git a/mr-clean-processor/src/test/java/com/trello/mrclean/MrCleanProcessorTest.kt b/mr-clean-processor/src/test/java/com/trello/mrclean/MrCleanProcessorTest.kt new file mode 100644 index 0000000..76b5cb3 --- /dev/null +++ b/mr-clean-processor/src/test/java/com/trello/mrclean/MrCleanProcessorTest.kt @@ -0,0 +1,157 @@ +package com.trello.mrclean + +import com.google.common.truth.Truth.assertThat +import com.tschuchort.compiletesting.KotlinCompilation +import com.tschuchort.compiletesting.KotlinCompilation.ExitCode +import com.tschuchort.compiletesting.SourceFile +import com.tschuchort.compiletesting.kspSourcesDir +import com.tschuchort.compiletesting.symbolProcessorProviders +import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi +import org.junit.Test +import java.io.File + +@OptIn(ExperimentalCompilerApi::class) +class MrCleanProcessorTest { + @Test + fun `default sanitizes string`() { + val kotlinSource = SourceFile.kotlin( + "KClass.kt", + """ + package com.test + import com.trello.mrclean.annotations.Sanitize + + @Sanitize + data class TwoParam(val bar: Int, val meow: Int) + """, + ) + val option = mapOf("mrclean.packagename" to "com.test") + val compilation = KotlinCompilation().apply { + sources = listOf(kotlinSource) + + // pass your own instance of an annotation processor + symbolProcessorProviders = listOf(TestMrCleanProcessorProvider(option)) + + inheritClassPath = true + messageOutputStream = System.out // see diagnostics in real time + } + val result = compilation.compile() + assertThat(result.exitCode).isEqualTo(ExitCode.OK) + val generatedSourcesDir = compilation.kspSourcesDir + + val generatedFile = + File(generatedSourcesDir, "kotlin/com/test/SanitizationForTest.TwoParam.kt") + assertThat(generatedFile.exists()).isTrue() + assertThat( + generatedFile.readText().trimIndent(), + ).isEqualTo( + """ +package com.test + +import kotlin.String +import kotlin.Suppress + +@Suppress("NOTHING_TO_INLINE") +internal inline fun TwoParam.sanitizedToString(): String = "TwoParam@${'$'}{hashCode().toString(16)}" + """.trimIndent(), + ) + } + + @Test + fun `default sanitizes private string`() { + val kotlinSource = SourceFile.kotlin( + "KClass.kt", + """ + package com.test + import com.trello.mrclean.annotations.Sanitize + + @Sanitize + data class TwoParam(private val bar: Int, val meow: Int) + """, + ) + val option = mapOf( + "mrclean.packagename" to "com.test", + "mrclean.debug" to "true", + ) + val compilation = KotlinCompilation().apply { + sources = listOf(kotlinSource) + + // pass your own instance of an annotation processor + symbolProcessorProviders = listOf(TestMrCleanProcessorProvider(option)) + + inheritClassPath = true + messageOutputStream = System.out // see diagnostics in real time + } + val result = compilation.compile() + assertThat(result.exitCode).isEqualTo(ExitCode.OK) + val generatedSourcesDir = compilation.kspSourcesDir + println(generatedSourcesDir) + + val generatedFile = + File(generatedSourcesDir, "kotlin/com/test/SanitizationForTest.TwoParam.kt") + assertThat(generatedFile.exists()).isTrue() + println(generatedFile.readText()) + assertThat( + generatedFile.readText().trimIndent(), + ).isEqualTo( + """ +package com.test + +import kotlin.String +import kotlin.Suppress + +@Suppress("NOTHING_TO_INLINE") +internal inline fun TwoParam.sanitizedToString(): String = + ""${'"'}TwoParam(bar = , meow = ${'$'}meow)""${'"'} + """.trimIndent().trim(), + ) + println(result) + } + + @Test + fun `root function generates`() { + val kotlinSource = SourceFile.kotlin( + "KClass.kt", + """ + package com.test + import com.trello.mrclean.annotations.Sanitize + + @Sanitize + data class TwoParam(private val bar: Int, val meow: Int) + """, + ) + val option = mapOf( + "mrclean.packagename" to "com.test", + "mrclean.rootgenerator" to "true", + ) + val compilation = KotlinCompilation().apply { + sources = listOf(kotlinSource) + + // pass your own instance of an annotation processor + symbolProcessorProviders = listOf(TestMrCleanProcessorProvider(option)) + + inheritClassPath = true + messageOutputStream = System.out // see diagnostics in real time + } + val result = compilation.compile() + assertThat(result.exitCode).isEqualTo(ExitCode.OK) + val generatedSourcesDir = compilation.kspSourcesDir + println(generatedSourcesDir) + + val generatedRootFile = + File(generatedSourcesDir, "kotlin/com/test/RootSanitizeFunction.kt") + + assertThat(generatedRootFile.exists()).isTrue() + assertThat(generatedRootFile.readText().trimIndent()).isEqualTo( + """ +// This is the root function that generated functions will overload +package com.test + +import kotlin.Any +import kotlin.String + +internal fun Any.sanitizedToString(): String = + error("No function generated! Make sure to annotate with @Sanitize") + """.trimIndent().trim(), + ) + } +} diff --git a/mr-clean-processor/src/test/java/com/trello/mrclean/TestMrCleanProcessorProvider.kt b/mr-clean-processor/src/test/java/com/trello/mrclean/TestMrCleanProcessorProvider.kt new file mode 100644 index 0000000..62990d4 --- /dev/null +++ b/mr-clean-processor/src/test/java/com/trello/mrclean/TestMrCleanProcessorProvider.kt @@ -0,0 +1,14 @@ +package com.trello.mrclean + +import com.google.devtools.ksp.processing.SymbolProcessor +import com.google.devtools.ksp.processing.SymbolProcessorEnvironment +import com.google.devtools.ksp.processing.SymbolProcessorProvider + +class TestMrCleanProcessorProvider(val defaultOpts: Map) : SymbolProcessorProvider { + override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor = + MrCleanProcessor( + codeGenerator = environment.codeGenerator, + options = environment.options + defaultOpts, + logger = environment.logger, + ) +} From f0bfdb12ef0be1a666cc787d7b57b7f079c42bbe Mon Sep 17 00:00:00 2001 From: Brian Parent Date: Fri, 10 Nov 2023 12:33:18 -0600 Subject: [PATCH 5/7] Fix build deps --- build.gradle | 7 ------- settings.gradle | 5 ----- settings.gradle.kts | 21 +++++++++++++++++++++ 3 files changed, 21 insertions(+), 12 deletions(-) delete mode 100644 settings.gradle create mode 100644 settings.gradle.kts diff --git a/build.gradle b/build.gradle index 39d98b0..a57277d 100644 --- a/build.gradle +++ b/build.gradle @@ -1,12 +1,5 @@ buildscript { allprojects { - repositories { - google() - mavenCentral() - mavenLocal() - jcenter() - } - group = GROUP version = VERSION_NAME } diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index 17abd8c..0000000 --- a/settings.gradle +++ /dev/null @@ -1,5 +0,0 @@ -include ':mr-clean-runtime' -include ':mr-clean-processor' -include ':mr-clean-processor-core' -include ':mr-clean-debug-processor' -include ':mr-clean-plugin' \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..a627af4 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,21 @@ +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +include(":mr-clean-runtime") +include(":mr-clean-processor") +include(":mr-clean-processor-core") +include(":mr-clean-debug-processor") +include(":mr-clean-plugin") From 361261cd9deb750e7323ab4c2f3d1c3ffe29c37d Mon Sep 17 00:00:00 2001 From: Brian Parent Date: Fri, 10 Nov 2023 13:21:08 -0600 Subject: [PATCH 6/7] Support generic types --- mr-clean-debug-processor/build.gradle.kts | 3 + .../mrclean/MrCleanDebugProcessorTest.kt | 70 ++++++ .../mrclean/TestMrCleanProcessorProvider.kt | 14 ++ .../trello/mrclean/MrCleanCoreProcessor.kt | 6 +- .../com/trello/mrclean/MrCleanDataClasses.kt | 9 +- .../java/com/trello/mrclean/MrCleanVisitor.kt | 7 +- .../com/trello/mrclean/SanitizeGenerator.kt | 12 +- .../trello/mrclean/SanitizeGeneratorTest.kt | 209 ------------------ .../trello/mrclean/MrCleanProcessorTest.kt | 102 ++++++++- settings.gradle.kts | 1 - 10 files changed, 214 insertions(+), 219 deletions(-) create mode 100644 mr-clean-debug-processor/src/test/java/com/trello/mrclean/MrCleanDebugProcessorTest.kt create mode 100644 mr-clean-debug-processor/src/test/java/com/trello/mrclean/TestMrCleanProcessorProvider.kt delete mode 100644 mr-clean-processor-core/src/test/java/com/trello/mrclean/SanitizeGeneratorTest.kt diff --git a/mr-clean-debug-processor/build.gradle.kts b/mr-clean-debug-processor/build.gradle.kts index ed4d16d..d7c4730 100644 --- a/mr-clean-debug-processor/build.gradle.kts +++ b/mr-clean-debug-processor/build.gradle.kts @@ -15,6 +15,9 @@ dependencies { implementation(libs.ksp) testImplementation(libs.junit) + testImplementation(libs.truth) + testImplementation(libs.zsweers.compileTesting.core) + testImplementation(libs.zsweers.compileTesting.ksp) } ksp { diff --git a/mr-clean-debug-processor/src/test/java/com/trello/mrclean/MrCleanDebugProcessorTest.kt b/mr-clean-debug-processor/src/test/java/com/trello/mrclean/MrCleanDebugProcessorTest.kt new file mode 100644 index 0000000..5b767dd --- /dev/null +++ b/mr-clean-debug-processor/src/test/java/com/trello/mrclean/MrCleanDebugProcessorTest.kt @@ -0,0 +1,70 @@ +package com.trello.mrclean + +import com.google.common.truth.Truth.assertThat +import com.tschuchort.compiletesting.KotlinCompilation +import com.tschuchort.compiletesting.KotlinCompilation.ExitCode +import com.tschuchort.compiletesting.SourceFile +import com.tschuchort.compiletesting.kspSourcesDir +import com.tschuchort.compiletesting.symbolProcessorProviders +import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi +import org.junit.Test +import java.io.File + +class MrCleanDebugProcessorTest { + + @OptIn(ExperimentalCompilerApi::class) + @Test + fun `debug default cleartext private string`() { + val kotlinSource = SourceFile.kotlin( + "KClass.kt", + """ + package com.test + import com.trello.mrclean.annotations.Sanitize + + @Sanitize + data class EightParam( + val one: Int, + val two: Int, + val three: Int, + val four: Int, + val five: Int, + val six: Int, + private val seven: Int, + val eight: Int, + ) + """, + ) + val option = mapOf( + "mrclean.packagename" to "com.test", + ) + val compilation = KotlinCompilation().apply { + sources = listOf(kotlinSource) + + // pass your own instance of an annotation processor + symbolProcessorProviders = listOf(TestMrCleanProcessorProvider(option)) + + inheritClassPath = true + messageOutputStream = System.out // see diagnostics in real time + } + val result = compilation.compile() + assertThat(result.exitCode).isEqualTo(ExitCode.OK) + val generatedSourcesDir = compilation.kspSourcesDir + val generatedFile = + File(generatedSourcesDir, "kotlin/com/test/SanitizationForTest.EightParam.kt") + assertThat(generatedFile.exists()).isTrue() + assertThat( + generatedFile.readText().trimIndent(), + ).isEqualTo( + """ +package com.test + +import kotlin.String +import kotlin.Suppress + +@Suppress("NOTHING_TO_INLINE") +internal inline fun EightParam.sanitizedToString(): String = + ""${'"'}EightParam(one = ${'$'}one, two = ${'$'}two, three = ${'$'}three, four = ${'$'}four, five = ${'$'}five, six = ${'$'}six, seven = , eight = ${'$'}eight)""${'"'} + """.trimIndent().trim(), + ) + } +} diff --git a/mr-clean-debug-processor/src/test/java/com/trello/mrclean/TestMrCleanProcessorProvider.kt b/mr-clean-debug-processor/src/test/java/com/trello/mrclean/TestMrCleanProcessorProvider.kt new file mode 100644 index 0000000..68b6b7b --- /dev/null +++ b/mr-clean-debug-processor/src/test/java/com/trello/mrclean/TestMrCleanProcessorProvider.kt @@ -0,0 +1,14 @@ +package com.trello.mrclean + +import com.google.devtools.ksp.processing.SymbolProcessor +import com.google.devtools.ksp.processing.SymbolProcessorEnvironment +import com.google.devtools.ksp.processing.SymbolProcessorProvider + +class TestMrCleanProcessorProvider(val defaultOpts: Map) : SymbolProcessorProvider { + override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor = + MrCleanDebugProcessor( + codeGenerator = environment.codeGenerator, + options = environment.options + defaultOpts, + logger = environment.logger, + ) +} diff --git a/mr-clean-processor-core/src/main/java/com/trello/mrclean/MrCleanCoreProcessor.kt b/mr-clean-processor-core/src/main/java/com/trello/mrclean/MrCleanCoreProcessor.kt index 0d1ba84..510b337 100644 --- a/mr-clean-processor-core/src/main/java/com/trello/mrclean/MrCleanCoreProcessor.kt +++ b/mr-clean-processor-core/src/main/java/com/trello/mrclean/MrCleanCoreProcessor.kt @@ -12,6 +12,7 @@ import com.google.devtools.ksp.validate import com.squareup.kotlinpoet.FileSpec import com.squareup.kotlinpoet.FunSpec import com.squareup.kotlinpoet.KModifier +import com.squareup.kotlinpoet.ksp.toClassName import com.squareup.kotlinpoet.ksp.writeTo import com.trello.mrclean.annotations.Sanitize @@ -54,7 +55,6 @@ class MrCleanCoreProcessor( val isDebug = debugFromOpts?.toBoolean() ?: false val generateRoot = options[ROOT_GENERATOR_KEY]?.toBoolean() ?: false - var symbolCount = 0 val classes = mutableListOf() symbols.forEach { @@ -62,9 +62,11 @@ class MrCleanCoreProcessor( it.accept(visitor, Unit) classes.add( MrCleanClassData( + className = it.toClassName(), qualifiedName = it.qualifiedName?.asString()!!, simpleName = it.simpleName.asString(), properties = visitor.properties, + classTypes = visitor.typeInfo, enclosingPackage = it.packageName.getShortName() .replaceFirstChar { first -> first.uppercase() }, qualifiedPackage = it.packageName.asString(), @@ -76,9 +78,11 @@ class MrCleanCoreProcessor( val map = classes.map { it to SanitizeGenerator.generateSanitizedToString( + it.className, it.qualifiedName, it.simpleName, it.properties, + it.classTypes, isDebug, ) } diff --git a/mr-clean-processor-core/src/main/java/com/trello/mrclean/MrCleanDataClasses.kt b/mr-clean-processor-core/src/main/java/com/trello/mrclean/MrCleanDataClasses.kt index 4faa178..bf68d8c 100644 --- a/mr-clean-processor-core/src/main/java/com/trello/mrclean/MrCleanDataClasses.kt +++ b/mr-clean-processor-core/src/main/java/com/trello/mrclean/MrCleanDataClasses.kt @@ -1,20 +1,25 @@ package com.trello.mrclean import com.google.devtools.ksp.symbol.KSFile +import com.squareup.kotlinpoet.ClassName +import com.squareup.kotlinpoet.TypeVariableName data class MrCleanClassData( + val className: ClassName, val qualifiedName: String, val simpleName: String, val qualifiedPackage: String, val enclosingPackage: String, val properties: List, + val classTypes: List, val originatingFile: KSFile?, ) { - fun getFileName() = "SanitizationFor$enclosingPackage${qualifiedName.removePrefix(qualifiedPackage)}" + fun getFileName() = + "SanitizationFor$enclosingPackage${qualifiedName.removePrefix(qualifiedPackage)}" } data class MrCleanProperty( val name: String, val type: String? = null, val isPublic: Boolean, -) \ No newline at end of file +) diff --git a/mr-clean-processor-core/src/main/java/com/trello/mrclean/MrCleanVisitor.kt b/mr-clean-processor-core/src/main/java/com/trello/mrclean/MrCleanVisitor.kt index 4e93a75..aa59798 100644 --- a/mr-clean-processor-core/src/main/java/com/trello/mrclean/MrCleanVisitor.kt +++ b/mr-clean-processor-core/src/main/java/com/trello/mrclean/MrCleanVisitor.kt @@ -7,11 +7,14 @@ import com.google.devtools.ksp.symbol.KSPropertyDeclaration import com.google.devtools.ksp.symbol.KSType import com.google.devtools.ksp.symbol.KSVisitorVoid import com.google.devtools.ksp.validate +import com.squareup.kotlinpoet.TypeVariableName +import com.squareup.kotlinpoet.ksp.toTypeParameterResolver class MrCleanVisitor( private val logger: KSPLogger, ) : KSVisitorVoid() { val properties = mutableListOf() + val typeInfo = mutableListOf() override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) { val properties: Sequence = classDeclaration.getAllProperties() .filter { it.validate() } @@ -19,6 +22,8 @@ class MrCleanVisitor( properties.forEach { visitPropertyDeclaration(it, Unit) } + val classTypeParams = classDeclaration.typeParameters.toTypeParameterResolver() + typeInfo.addAll(classTypeParams.parametersMap.values) } override fun visitPropertyDeclaration(property: KSPropertyDeclaration, data: Unit) { @@ -29,4 +34,4 @@ class MrCleanVisitor( val type = resolvedType.declaration.qualifiedName?.asString() properties += MrCleanProperty(propertyName, type, isPublic) } -} \ No newline at end of file +} diff --git a/mr-clean-processor-core/src/main/java/com/trello/mrclean/SanitizeGenerator.kt b/mr-clean-processor-core/src/main/java/com/trello/mrclean/SanitizeGenerator.kt index ff24936..c4b57c7 100644 --- a/mr-clean-processor-core/src/main/java/com/trello/mrclean/SanitizeGenerator.kt +++ b/mr-clean-processor-core/src/main/java/com/trello/mrclean/SanitizeGenerator.kt @@ -5,14 +5,23 @@ import com.squareup.kotlinpoet.ClassName import com.squareup.kotlinpoet.CodeBlock import com.squareup.kotlinpoet.FunSpec import com.squareup.kotlinpoet.KModifier +import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy +import com.squareup.kotlinpoet.TypeVariableName internal object SanitizeGenerator { fun generateSanitizedToString( + className: ClassName, qualifiedClassName: String, simpleClassName: String, properties: List, + classTypes: List, isDebug: Boolean, ): FunSpec { + val parameteizedClassName = if (classTypes.isEmpty()) { + className + } else { + className.parameterizedBy(classTypes) + } val debugString = properties.joinToString { if (it.isPublic) "${it.name} = ${"$"}${it.name}" else "${it.name} = " } @@ -30,7 +39,8 @@ internal object SanitizeGenerator { .build() val build = FunSpec.builder("sanitizedToString") .addAnnotation(suppressAnnotation) - .receiver(ClassName.bestGuess(qualifiedClassName)) + .receiver(parameteizedClassName) + .addTypeVariables(classTypes.map { TypeVariableName(it.name, it.bounds, null) }) .addModifiers(KModifier.INLINE, KModifier.INTERNAL) .returns(String::class) .apply { diff --git a/mr-clean-processor-core/src/test/java/com/trello/mrclean/SanitizeGeneratorTest.kt b/mr-clean-processor-core/src/test/java/com/trello/mrclean/SanitizeGeneratorTest.kt deleted file mode 100644 index 8a9562c..0000000 --- a/mr-clean-processor-core/src/test/java/com/trello/mrclean/SanitizeGeneratorTest.kt +++ /dev/null @@ -1,209 +0,0 @@ -package com.trello.mrclean -import com.squareup.kotlinpoet.FileSpec -import com.squareup.kotlinpoet.FunSpec -import org.junit.Assert.* -import org.junit.Test - -class SanitizeGeneratorTest { - - data class TwoParam(val bar: Int, val meow: Int) - - @Test - fun generateDebugSanitizedToString() { - val properties = listOf( - MrCleanProperty(isPublic = true, name = "bar"), - MrCleanProperty(isPublic = true, name = "meow"), - ) - val qualifiedClassName = TwoParam::class.java.canonicalName - val simpleClassName = TwoParam::class.java.simpleName - val expectedOuput = """ - |package com.example - - |import com.trello.mrclean.SanitizeGeneratorTest - |import kotlin.String - |import kotlin.Suppress - - |@Suppress("NOTHING_TO_INLINE") - |internal inline fun SanitizeGeneratorTest.TwoParam.sanitizedToString(): String = - | ${"\""}${"\""}${"\""}TwoParam(bar = ${"$"}bar, meow = ${"$"}meow)${"\""}${"\""}${"\""} - | - """.trimMargin() - - assertEquals( - expectedOuput, - buildFile( - SanitizeGenerator.generateSanitizedToString( - qualifiedClassName, - simpleClassName, - properties, - true, - ), - ), - ) - } - - @Test - fun generateReleaseSanitizedToString() { - val properties = listOf( - MrCleanProperty(isPublic = true, name = "bar"), - MrCleanProperty(isPublic = true, name = "meow"), - ) - val qualifiedClassName = TwoParam::class.java.canonicalName - val simpleClassName = TwoParam::class.java.simpleName - val expectedOuput = """ - |package com.example - - |import com.trello.mrclean.SanitizeGeneratorTest - |import kotlin.String - |import kotlin.Suppress - - |@Suppress("NOTHING_TO_INLINE") - |internal inline fun SanitizeGeneratorTest.TwoParam.sanitizedToString(): String = - | "TwoParam@${"$"}{hashCode().toString(16)}" - | - """.trimMargin() - - assertEquals( - expectedOuput, - buildFile( - SanitizeGenerator.generateSanitizedToString( - qualifiedClassName, - simpleClassName, - properties, - false, - ), - ), - ) - } - - data class EightParam( - val one: Int, - val two: Int, - val three: Int, - val four: Int, - val five: Int, - val six: Int, - val seven: Int, - val eight: Int, - ) - - @Test - fun generateReleaseSanitizedToStringForLongClassHeader() { - val properties = listOf( - MrCleanProperty(isPublic = true, name = "one"), - MrCleanProperty(isPublic = true, name = "two"), - MrCleanProperty(isPublic = true, name = "three"), - MrCleanProperty(isPublic = true, name = "four"), - MrCleanProperty(isPublic = true, name = "five"), - MrCleanProperty(isPublic = true, name = "six"), - MrCleanProperty(isPublic = true, name = "seven"), - MrCleanProperty(isPublic = true, name = "eight"), - ) - val qualifiedClassName = EightParam::class.java.canonicalName - val simpleClassName = EightParam::class.java.simpleName - - val expectedOuput = """ - |package com.example - - |import com.trello.mrclean.SanitizeGeneratorTest - |import kotlin.String - |import kotlin.Suppress - - |@Suppress("NOTHING_TO_INLINE") - |internal inline fun SanitizeGeneratorTest.EightParam.sanitizedToString(): String = - | "EightParam@${"$"}{hashCode().toString(16)}" - | - """.trimMargin() - val output = buildFile( - SanitizeGenerator.generateSanitizedToString( - qualifiedClassName, - simpleClassName, - properties, - false, - ), - ) - - assertEquals(expectedOuput, output) - } - - @Test - fun generateDebugSanitizedToStringForLongClassHeader() { - val properties = listOf( - MrCleanProperty(isPublic = true, name = "one"), - MrCleanProperty(isPublic = true, name = "two"), - MrCleanProperty(isPublic = true, name = "three"), - MrCleanProperty(isPublic = true, name = "four"), - MrCleanProperty(isPublic = true, name = "five"), - MrCleanProperty(isPublic = true, name = "six"), - MrCleanProperty(isPublic = true, name = "seven"), - MrCleanProperty(isPublic = true, name = "eight"), - ) - val qualifiedClassName = EightParam::class.java.canonicalName - val simpleClassName = EightParam::class.java.simpleName - - val expectedOuput = """ - |package com.example - - |import com.trello.mrclean.SanitizeGeneratorTest - |import kotlin.String - |import kotlin.Suppress - - |@Suppress("NOTHING_TO_INLINE") - |internal inline fun SanitizeGeneratorTest.EightParam.sanitizedToString(): String = - | ${"\""}${"\""}${"\""}EightParam(one = ${"$"}one, two = ${"$"}two, three = ${"$"}three, four = ${"$"}four, five = ${"$"}five, six = ${"$"}six, seven = ${"$"}seven, eight = ${"$"}eight)${"\""}${"\""}${"\""} - | - """.trimMargin() - val output = buildFile( - SanitizeGenerator.generateSanitizedToString( - qualifiedClassName, - simpleClassName, - properties, - true, - ), - ) - - assertEquals(expectedOuput, output) - } - - private fun buildFile(funSpec: FunSpec): String { - val output = StringBuilder() - FileSpec.builder("com.example", "Sanitizations") - .addFunction(funSpec) - .build() - .writeTo(output) - return output.toString() - } - - @Test - fun generateDebugSanitizedToStringIgnoresPrivateProperties() { - val properties = listOf( - MrCleanProperty(isPublic = false, name = "bar"), - MrCleanProperty(isPublic = true, name = "meow"), - ) - val qualifiedClassName = TwoParam::class.java.canonicalName - val simpleClassName = TwoParam::class.java.simpleName - val expectedOuput = """ - |package com.example - - |import com.trello.mrclean.SanitizeGeneratorTest - |import kotlin.String - |import kotlin.Suppress - - |@Suppress("NOTHING_TO_INLINE") - |internal inline fun SanitizeGeneratorTest.TwoParam.sanitizedToString(): String = - | ${"\""}${"\""}${"\""}TwoParam(bar = , meow = ${"$"}meow)${"\""}${"\""}${"\""} - | - """.trimMargin() - assertEquals( - expectedOuput, - buildFile( - SanitizeGenerator.generateSanitizedToString( - qualifiedClassName, - simpleClassName, - properties, - true, - ), - ), - ) - } -} \ No newline at end of file diff --git a/mr-clean-processor/src/test/java/com/trello/mrclean/MrCleanProcessorTest.kt b/mr-clean-processor/src/test/java/com/trello/mrclean/MrCleanProcessorTest.kt index 76b5cb3..087f26c 100644 --- a/mr-clean-processor/src/test/java/com/trello/mrclean/MrCleanProcessorTest.kt +++ b/mr-clean-processor/src/test/java/com/trello/mrclean/MrCleanProcessorTest.kt @@ -84,12 +84,10 @@ internal inline fun TwoParam.sanitizedToString(): String = "TwoParam@${'$'}{hash val result = compilation.compile() assertThat(result.exitCode).isEqualTo(ExitCode.OK) val generatedSourcesDir = compilation.kspSourcesDir - println(generatedSourcesDir) val generatedFile = File(generatedSourcesDir, "kotlin/com/test/SanitizationForTest.TwoParam.kt") assertThat(generatedFile.exists()).isTrue() - println(generatedFile.readText()) assertThat( generatedFile.readText().trimIndent(), ).isEqualTo( @@ -104,7 +102,6 @@ internal inline fun TwoParam.sanitizedToString(): String = ""${'"'}TwoParam(bar = , meow = ${'$'}meow)""${'"'} """.trimIndent().trim(), ) - println(result) } @Test @@ -135,7 +132,6 @@ internal inline fun TwoParam.sanitizedToString(): String = val result = compilation.compile() assertThat(result.exitCode).isEqualTo(ExitCode.OK) val generatedSourcesDir = compilation.kspSourcesDir - println(generatedSourcesDir) val generatedRootFile = File(generatedSourcesDir, "kotlin/com/test/RootSanitizeFunction.kt") @@ -154,4 +150,102 @@ internal fun Any.sanitizedToString(): String = """.trimIndent().trim(), ) } + + @Test + fun `test generic support`() { + val kotlinSource = SourceFile.kotlin( + "KClass.kt", + """ + package com.test + import com.trello.mrclean.annotations.Sanitize + + @Sanitize + data class TwoParam, in V, out R, E : T>(val bar: T, val meow: Int) + """, + ) + val option = mapOf( + "mrclean.packagename" to "com.test", + "mrclean.debug" to "true", + "mrclean.rootgenerator" to "true", + ) + val compilation = KotlinCompilation().apply { + sources = listOf(kotlinSource) + + // pass your own instance of an annotation processor + symbolProcessorProviders = listOf(TestMrCleanProcessorProvider(option)) + + inheritClassPath = true + messageOutputStream = System.out // see diagnostics in real time + } + val result = compilation.compile() + assertThat(result.exitCode).isEqualTo(ExitCode.OK) + val generatedSourcesDir = compilation.kspSourcesDir + + val generatedFile = + File(generatedSourcesDir, "kotlin/com/test/SanitizationForTest.TwoParam.kt") + assertThat(generatedFile.exists()).isTrue() + + assertThat(generatedFile.exists()).isTrue() + assertThat(generatedFile.readText().trimIndent()).isEqualTo( + """ +package com.test + +import kotlin.String +import kotlin.Suppress +import kotlin.collections.List + +@Suppress("NOTHING_TO_INLINE") +internal inline fun , V, R, E : T> TwoParam.sanitizedToString(): + String = ""${'"'}TwoParam(bar = ${'$'}bar, meow = ${'$'}meow)""${'"'} + """.trimIndent().trim(), + ) + } + + @Test + fun `test generic support private`() { + val kotlinSource = SourceFile.kotlin( + "KClass.kt", + """ + package com.test + import com.trello.mrclean.annotations.Sanitize + + @Sanitize + data class TwoParam, in V, out R, E : T>(private val bar: T, val meow: Int) + """, + ) + val option = mapOf( + "mrclean.packagename" to "com.test", + ) + val compilation = KotlinCompilation().apply { + sources = listOf(kotlinSource) + + // pass your own instance of an annotation processor + symbolProcessorProviders = listOf(TestMrCleanProcessorProvider(option)) + + inheritClassPath = true + messageOutputStream = System.out // see diagnostics in real time + } + val result = compilation.compile() + assertThat(result.exitCode).isEqualTo(ExitCode.OK) + val generatedSourcesDir = compilation.kspSourcesDir + + val generatedFile = + File(generatedSourcesDir, "kotlin/com/test/SanitizationForTest.TwoParam.kt") + assertThat(generatedFile.exists()).isTrue() + + assertThat(generatedFile.exists()).isTrue() + assertThat(generatedFile.readText().trimIndent()).isEqualTo( + """ +package com.test + +import kotlin.String +import kotlin.Suppress +import kotlin.collections.List + +@Suppress("NOTHING_TO_INLINE") +internal inline fun , V, R, E : T> TwoParam.sanitizedToString(): + String = "TwoParam@${'$'}{hashCode().toString(16)}" + """.trimIndent().trim(), + ) + } } diff --git a/settings.gradle.kts b/settings.gradle.kts index a627af4..3303ab9 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -7,7 +7,6 @@ pluginManagement { } dependencyResolutionManagement { - repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() From 9eba2c835c9345ce143678e10124a0834e9015e2 Mon Sep 17 00:00:00 2001 From: Brian Parent Date: Fri, 10 Nov 2023 15:15:21 -0600 Subject: [PATCH 7/7] Cleaned up some sloppiness --- gradle/libs.versions.toml | 17 ++++++++++------- .../com/trello/mrclean/MrCleanDebugProcessor.kt | 8 ++++++-- .../com/trello/mrclean/plugin/MrCleanPlugin.kt | 15 +++++++-------- .../com/trello/mrclean/annotations/Sanitize.kt | 1 - 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 43f3f35..0d5e76f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,4 +1,5 @@ [versions] +android-runtime = "4.1.1.4" activity-compose = "1.8.0" autoservice = "1.1.1" autoserviceKsp = "1.1.0" @@ -10,31 +11,33 @@ agp = "8.1.2" org-jetbrains-kotlin-android = "1.9.10" junit = "4.13.2" compileTesting = "1.5.0" +truth = "1.1.4" +kctfork = "0.2.1" +vanniktech-publish = "0.25.3" [libraries] autoservice-ksp = { module = "dev.zacsweers.autoservice:auto-service-ksp", version.ref = "autoserviceKsp" } autoservice-annotations = { module = "com.google.auto.service:auto-service-annotations", version.ref = "autoservice" } -android-runtime = "com.google.android:android:4.1.1.4" +android-runtime = { module = "com.google.android:android", version.ref = "android-runtime" } android-gradlePlugin = { module = "com.android.tools.build:gradle", version.ref = "agp" } -junit = "junit:junit:4.13.2" kotlinPoet-core = { module = "com.squareup:kotlinpoet", version.ref = "kotlinPoet" } kotlinPoet-ksp = { module = "com.squareup:kotlinpoet-ksp", version.ref = "kotlinPoet" } kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk7", version.ref = "kotlin" } ksp = { module = "com.google.devtools.ksp:symbol-processing-api ", version.ref = "ksp" } plugin-ksp = { module = "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" } -junit-junit = { group = "junit", name = "junit", version.ref = "junit" } -truth = { module ="com.google.truth:truth", version = "1.1.4" } +junit = { group = "junit", name = "junit", version.ref = "junit" } +truth = { module ="com.google.truth:truth", version.ref = "truth" } compileTesting-core = { module = "com.github.tschuchortdev:kotlin-compile-testing", version.ref = "compileTesting" } compileTesting-ksp = { module = "com.github.tschuchortdev:kotlin-compile-testing-ksp", version.ref = "compileTesting" } -zsweers-compileTesting-core = { module = "dev.zacsweers.kctfork:core", version = "0.2.1"} -zsweers-compileTesting-ksp= { module = "dev.zacsweers.kctfork:ksp", version = "0.2.1"} +zsweers-compileTesting-core = { module = "dev.zacsweers.kctfork:core", version.ref = "kctfork"} +zsweers-compileTesting-ksp= { module = "dev.zacsweers.kctfork:ksp", version.ref = "kctfork"} [plugins] versions = { id = "com.github.ben-manes.versions", version.ref = "versionsPlugin" } kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } -vanniktech-publish = { id = "com.vanniktech.maven.publish", version = "0.25.3" } +vanniktech-publish = { id = "com.vanniktech.maven.publish", version.ref = "vanniktech-publish" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } com-android-application = { id = "com.android.application", version.ref = "agp" } org-jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "org-jetbrains-kotlin-android" } \ No newline at end of file diff --git a/mr-clean-debug-processor/src/main/java/com/trello/mrclean/MrCleanDebugProcessor.kt b/mr-clean-debug-processor/src/main/java/com/trello/mrclean/MrCleanDebugProcessor.kt index 421d457..724b23a 100644 --- a/mr-clean-debug-processor/src/main/java/com/trello/mrclean/MrCleanDebugProcessor.kt +++ b/mr-clean-debug-processor/src/main/java/com/trello/mrclean/MrCleanDebugProcessor.kt @@ -1,7 +1,11 @@ package com.trello.mrclean -import com.google.devtools.ksp.processing.* -import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.processing.CodeGenerator +import com.google.devtools.ksp.processing.KSPLogger +import com.google.devtools.ksp.processing.Resolver +import com.google.devtools.ksp.processing.SymbolProcessor +import com.google.devtools.ksp.symbol.KSAnnotated + class MrCleanDebugProcessor( val codeGenerator: CodeGenerator, diff --git a/mr-clean-plugin/src/main/java/com/trello/mrclean/plugin/MrCleanPlugin.kt b/mr-clean-plugin/src/main/java/com/trello/mrclean/plugin/MrCleanPlugin.kt index f8c86e5..23e62c8 100644 --- a/mr-clean-plugin/src/main/java/com/trello/mrclean/plugin/MrCleanPlugin.kt +++ b/mr-clean-plugin/src/main/java/com/trello/mrclean/plugin/MrCleanPlugin.kt @@ -1,7 +1,7 @@ package com.trello.mrclean.plugin import com.android.build.api.variant.AndroidComponentsExtension -import com.android.build.gradle.* +import com.android.build.gradle.BaseExtension import com.android.build.gradle.internal.manifest.parseManifest import com.android.builder.errors.EvalIssueException import com.android.builder.errors.IssueReporter @@ -44,12 +44,12 @@ class MrCleanPlugin : Plugin { kspExtension.arg("mrclean.packagename", packageName) val taskName = "generate${variant.name.capitalize()}RootSanitizeFunction" val outputDir = project.buildDir.resolve("generated/source/mrclean/${variant.name}") - log.info("MrClean: task $taskName using directory $outputDir") + log.debug("MrClean: task $taskName using directory $outputDir") val task = project.tasks.register(taskName, GenerateRootFunctions::class.java) { it.outputDir.set(outputDir) it.packageName.set(packageName) } - log.info("MrClean: setting ${variant.sources.java} to ${GenerateRootFunctions::outputDir}") + log.debug("MrClean: setting ${variant.sources.java} to ${GenerateRootFunctions::outputDir}") variant.sources.java?.addGeneratedSourceDirectory( task, GenerateRootFunctions::outputDir, @@ -60,11 +60,10 @@ class MrCleanPlugin : Plugin { private fun getOrCreateKsp(project: Project, name: String): KspExtension { val hasKsp = project.extensions.findByName(name) != null return if (hasKsp) { - log.info("MrClean: found $name") + log.debug("MrClean: found $name") project.extensions.getByName(name) as KspExtension -// project.extensions.getByType(KspExtension::class.java) } else { - log.info("MrClean: trying to create $name") + log.debug("MrClean: trying to create $name") project.extensions.create(name, KspExtension::class.java) } } @@ -80,14 +79,14 @@ class MrCleanPlugin : Plugin { } else { "com.trello.mrclean:mr-clean-processor" } - log.info("Mr Clean is adding: ksp$configuration $coordinates:$VERSION") + log.debug("Mr Clean is adding: ksp$configuration $coordinates:$VERSION") val kspDep = project.configurations.getByName("ksp$configuration").dependencies kspDep.add(project.dependencies.create("$coordinates:$VERSION")) } private fun getPackageNameBase(extension: BaseExtension): String { return if (extension.namespace != null) { - log.info("using namespace ${extension.namespace}") + log.debug("using namespace ${extension.namespace}") extension.namespace!! } else { log.debug("couldn't find a namespace") diff --git a/mr-clean-runtime/src/main/java/com/trello/mrclean/annotations/Sanitize.kt b/mr-clean-runtime/src/main/java/com/trello/mrclean/annotations/Sanitize.kt index 2bb6f88..e4d451a 100644 --- a/mr-clean-runtime/src/main/java/com/trello/mrclean/annotations/Sanitize.kt +++ b/mr-clean-runtime/src/main/java/com/trello/mrclean/annotations/Sanitize.kt @@ -28,5 +28,4 @@ package com.trello.mrclean.annotations * ``` */ @Target(AnnotationTarget.CLASS) -@Retention(AnnotationRetention.RUNTIME) annotation class Sanitize