-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Migrate :libs:processors to ksp
#20217
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
53a4dd0
build: bump kotlin to 1.9.22, introduce ksp in libs:processors
wzieba 5490e4e
refactor: rewrite `RemoteConfigProcessor` to ksp
wzieba 80a80aa
feat: specify correct `originatingFiles` to optimize ksp output inval…
wzieba 55199e2
fix: do not generate classes if inputs are empty
wzieba cec55db
Merge branch 'kapt_processing_tests' into migrate_processors_to_ksp
wzieba baa6ce5
Merge branch 'kapt_processing_tests' into migrate_processors_to_ksp
wzieba 29409bc
test: adjust test setup for ksp usage
wzieba dd3a863
Merge branch 'ksp' into migrate_processors_to_ksp
wzieba dfcf761
tests: simplify RemoteConfigProcessorTest
wzieba 0292847
docs: add single-round processor comment
wzieba 3df71cf
style: fix checkstyle
wzieba File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
217 changes: 110 additions & 107 deletions
217
libs/processors/src/main/java/org/wordpress/android/processor/RemoteConfigProcessor.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,128 +1,131 @@ | ||
| @file:OptIn(KspExperimental::class) | ||
|
|
||
|
|
||
| package org.wordpress.android.processor | ||
|
|
||
| import com.google.auto.service.AutoService | ||
| import com.squareup.kotlinpoet.DelicateKotlinPoetApi | ||
| import com.squareup.kotlinpoet.TypeName | ||
| import com.squareup.kotlinpoet.asTypeName | ||
| import com.google.devtools.ksp.KspExperimental | ||
| import com.google.devtools.ksp.containingFile | ||
| import com.google.devtools.ksp.getAnnotationsByType | ||
| import com.google.devtools.ksp.processing.CodeGenerator | ||
| import com.google.devtools.ksp.processing.Resolver | ||
| import com.google.devtools.ksp.processing.SymbolProcessor | ||
| import com.google.devtools.ksp.symbol.KSAnnotated | ||
| import com.google.devtools.ksp.symbol.KSClassDeclaration | ||
| import com.squareup.kotlinpoet.ksp.toTypeName | ||
| import com.squareup.kotlinpoet.ksp.writeTo | ||
| import org.wordpress.android.annotation.Experiment | ||
| import org.wordpress.android.annotation.Feature | ||
| import org.wordpress.android.annotation.FeatureInDevelopment | ||
| import org.wordpress.android.annotation.RemoteFieldDefaultGenerater | ||
| import java.io.File | ||
| import javax.annotation.processing.AbstractProcessor | ||
| import javax.annotation.processing.Processor | ||
| import javax.annotation.processing.RoundEnvironment | ||
| import javax.annotation.processing.SupportedAnnotationTypes | ||
| import javax.annotation.processing.SupportedSourceVersion | ||
| import javax.lang.model.SourceVersion | ||
| import javax.lang.model.element.TypeElement | ||
| import javax.tools.Diagnostic.Kind | ||
|
|
||
| @AutoService(Processor::class) // For registering the service | ||
| @SupportedSourceVersion(SourceVersion.RELEASE_8) // to support Java 8 | ||
| @SupportedAnnotationTypes( | ||
| "org.wordpress.android.annotation.Experiment", | ||
| "org.wordpress.android.annotation.Feature", | ||
| "org.wordpress.android.annotation.FeatureInDevelopment", | ||
| "org.wordpress.android.annotation.RemoteFieldDefaultGenerater" | ||
| ) | ||
| class RemoteConfigProcessor : AbstractProcessor() { | ||
| @OptIn(DelicateKotlinPoetApi::class) | ||
| @Suppress("DEPRECATION") | ||
| override fun process(p0: MutableSet<out TypeElement>?, roundEnvironment: RoundEnvironment?): Boolean { | ||
| val experiments = roundEnvironment?.getElementsAnnotatedWith(Experiment::class.java)?.map { element -> | ||
| val annotation = element.getAnnotation(Experiment::class.java) | ||
| annotation.remoteField to annotation.defaultVariant | ||
| } ?: listOf() | ||
| val remoteFeatureNames = mutableListOf<TypeName>() | ||
| val features = roundEnvironment?.getElementsAnnotatedWith(Feature::class.java)?.map { element -> | ||
| val annotation = element.getAnnotation(Feature::class.java) | ||
| remoteFeatureNames.add(element.asType().asTypeName()) | ||
| annotation.remoteField to annotation.defaultValue.toString() | ||
| } ?: listOf() | ||
| val remoteFields = roundEnvironment?.getElementsAnnotatedWith(RemoteFieldDefaultGenerater::class.java) | ||
| ?.map { element -> | ||
| val annotation = element.getAnnotation(RemoteFieldDefaultGenerater::class.java) | ||
| annotation.remoteField to annotation.defaultValue | ||
| } ?: listOf() | ||
| val featuresInDevelopment = roundEnvironment?.getElementsAnnotatedWith(FeatureInDevelopment::class.java) | ||
| ?.map { element -> | ||
| element.asType().toString() | ||
| } ?: listOf() | ||
| return if (experiments.isNotEmpty() || features.isNotEmpty()) { | ||
| generateRemoteFieldConfigDefaults(remoteFields.toMap()) | ||
| generateRemoteFeatureConfigDefaults((experiments + features).toMap()) | ||
| generateRemoteFeatureConfigCheck(remoteFeatureNames) | ||
| generateFeaturesInDevelopment(featuresInDevelopment) | ||
| true | ||
| } else { | ||
| false | ||
|
|
||
| @OptIn(KspExperimental::class) | ||
| class RemoteConfigProcessor( | ||
| private val codeGenerator: CodeGenerator, | ||
| ) : SymbolProcessor { | ||
| /** | ||
| * In the case of this processor, we only one need round. Generated files do not depend on each other | ||
| * or any other processor. | ||
| * | ||
| * See: https://github.com/google/ksp/issues/797#issuecomment-1041127747 | ||
| * Also: https://github.com/google/ksp/blob/a0cd7774a7f65cec45a50ecc8960ef5e4d47fc21/examples/playground/test-processor/src/main/kotlin/TestProcessor.kt#L20 | ||
| */ | ||
| private var invoked = false | ||
|
|
||
| override fun process(resolver: Resolver): List<KSAnnotated> { | ||
| if (invoked) { | ||
| return emptyList() | ||
| } | ||
|
|
||
| val remoteFeatures = resolver.getSymbolsWithAnnotation("org.wordpress.android.annotation.Feature") | ||
| .toList() | ||
|
|
||
| generateRemoteFeatureConfigDefaults(resolver, remoteFeatures) | ||
| generateRemoteFieldsConfigDefaults(resolver) | ||
| generateFeaturesInDevelopment(resolver) | ||
| generateRemoteFeatureConfigCheck(remoteFeatures) | ||
|
|
||
| invoked = true | ||
| return emptyList() | ||
| } | ||
|
|
||
| @Suppress("TooGenericExceptionCaught", "SwallowedException") | ||
| private fun generateRemoteFeatureConfigDefaults( | ||
| remoteConfigDefaults: Map<String, String> | ||
| ) { | ||
| try { | ||
| val fileContent = RemoteFeatureConfigDefaultsBuilder(remoteConfigDefaults).getContent() | ||
|
|
||
| val kaptKotlinGeneratedDir = processingEnv.options[KAPT_KOTLIN_GENERATED_OPTION_NAME] | ||
| fileContent.writeTo(File(kaptKotlinGeneratedDir)) | ||
| } catch (e: Exception) { | ||
| processingEnv.messager.printMessage(Kind.ERROR, "Failed to generate remote feature config defaults") | ||
| private fun generateRemoteFeatureConfigDefaults(resolver: Resolver, remoteFeatures: List<KSAnnotated>) { | ||
| val experiments = resolver.getSymbolsWithAnnotation("org.wordpress.android.annotation.Experiment") | ||
| .toList() | ||
|
|
||
| val defaults = (remoteFeatures + experiments) | ||
| .map { element: KSAnnotated -> | ||
| val featuresDefaults = element.getAnnotationsByType(Feature::class) | ||
| .toList().associate { annotation -> | ||
| annotation.remoteField to annotation.defaultValue.toString() | ||
| } | ||
| val experimentsDefaults = element.getAnnotationsByType(Experiment::class).toList() | ||
| .toList().associate { annotation -> | ||
| annotation.remoteField to annotation.defaultVariant | ||
| } | ||
| featuresDefaults + experimentsDefaults | ||
| }.flatMap { it.toList() } | ||
| .toMap() | ||
|
|
||
| if (defaults.isNotEmpty()) { | ||
| RemoteFeatureConfigDefaultsBuilder(defaults).getContent() | ||
| .writeTo( | ||
| codeGenerator, | ||
| aggregating = true, | ||
| originatingKSFiles = remoteFeatures.map { it.containingFile!! } | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| @Suppress("TooGenericExceptionCaught", "SwallowedException") | ||
| private fun generateRemoteFieldConfigDefaults( | ||
| remoteConfigDefaults: Map<String, String> | ||
| ) { | ||
| try { | ||
| val fileContent = RemoteFieldConfigDefaultsBuilder(remoteConfigDefaults).getContent() | ||
|
|
||
| val kaptKotlinGeneratedDir = processingEnv.options[KAPT_KOTLIN_GENERATED_OPTION_NAME] | ||
| fileContent.writeTo(File(kaptKotlinGeneratedDir)) | ||
| } catch (e: Exception) { | ||
| processingEnv.messager.printMessage(Kind.ERROR, "Failed to generate remote feature config defaults") | ||
| private fun generateRemoteFieldsConfigDefaults(resolver: Resolver) { | ||
| val remoteFields = | ||
| resolver.getSymbolsWithAnnotation("org.wordpress.android.annotation.RemoteFieldDefaultGenerater") | ||
| .toList() | ||
| val remoteFieldDefaults = remoteFields | ||
| .associate { element: KSAnnotated -> | ||
| element.getAnnotationsByType(RemoteFieldDefaultGenerater::class) | ||
| .toList() | ||
| .first() | ||
| .let { annotation -> | ||
| annotation.remoteField to annotation.defaultValue | ||
| } | ||
| } | ||
|
|
||
| if(remoteFieldDefaults.isNotEmpty()) { | ||
| RemoteFieldConfigDefaultsBuilder(remoteFieldDefaults).getContent() | ||
| .writeTo( | ||
| codeGenerator, | ||
| aggregating = true, | ||
| originatingKSFiles = remoteFields.map { it.containingFile!! } | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| @Suppress("TooGenericExceptionCaught") | ||
| private fun generateRemoteFeatureConfigCheck( | ||
| remoteFeatureNames: List<TypeName> | ||
| ) { | ||
| try { | ||
| val fileContent = RemoteFeatureConfigCheckBuilder(remoteFeatureNames).getContent() | ||
|
|
||
| val kaptKotlinGeneratedDir = processingEnv.options[KAPT_KOTLIN_GENERATED_OPTION_NAME] | ||
| fileContent.writeTo(File(kaptKotlinGeneratedDir)) | ||
| } catch (e: Exception) { | ||
| processingEnv.messager.printMessage( | ||
| Kind.ERROR, | ||
| "Failed to generate remote feature config check: $e" | ||
| ) | ||
| private fun generateFeaturesInDevelopment(resolver: Resolver) { | ||
| val featuresInDevelopment = | ||
| resolver.getSymbolsWithAnnotation("org.wordpress.android.annotation.FeatureInDevelopment") | ||
| .filterIsInstance<KSClassDeclaration>() | ||
| .toList() | ||
| val featuresInDevelopmentDefaults = featuresInDevelopment | ||
| .map { it.simpleName.asString() } | ||
|
|
||
| if(featuresInDevelopmentDefaults.isNotEmpty()) { | ||
| FeaturesInDevelopmentDefaultsBuilder(featuresInDevelopmentDefaults).getContent() | ||
| .writeTo( | ||
| codeGenerator, | ||
| aggregating = true, | ||
| originatingKSFiles = featuresInDevelopment.map { it.containingFile!! } | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| @Suppress("TooGenericExceptionCaught") | ||
| private fun generateFeaturesInDevelopment( | ||
| remoteFeatureNames: List<String> | ||
| ) { | ||
| try { | ||
| val fileContent = FeaturesInDevelopmentDefaultsBuilder(remoteFeatureNames).getContent() | ||
|
|
||
| val kaptKotlinGeneratedDir = processingEnv.options[KAPT_KOTLIN_GENERATED_OPTION_NAME] | ||
| fileContent.writeTo(File(kaptKotlinGeneratedDir)) | ||
| } catch (e: Exception) { | ||
| processingEnv.messager.printMessage( | ||
| Kind.ERROR, | ||
| "Failed to generate remote config check: $e" | ||
| private fun generateRemoteFeatureConfigCheck(remoteFeatures: List<KSAnnotated>) { | ||
| if(remoteFeatures.isNotEmpty()) { | ||
| RemoteFeatureConfigCheckBuilder( | ||
| remoteFeatures.filterIsInstance<KSClassDeclaration>().map { it.asType(emptyList()).toTypeName() } | ||
| ).getContent().writeTo( | ||
| codeGenerator, | ||
| aggregating = true, | ||
| originatingKSFiles = remoteFeatures.map { it.containingFile!! } | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| companion object { | ||
| const val KAPT_KOTLIN_GENERATED_OPTION_NAME = "kapt.kotlin.generated" | ||
| } | ||
| } | ||
13 changes: 13 additions & 0 deletions
13
...processors/src/main/java/org/wordpress/android/processor/RemoteConfigProcessorProvider.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| package org.wordpress.android.processor | ||
|
|
||
| import com.google.devtools.ksp.processing.SymbolProcessor | ||
| import com.google.devtools.ksp.processing.SymbolProcessorEnvironment | ||
| import com.google.devtools.ksp.processing.SymbolProcessorProvider | ||
|
|
||
| class RemoteConfigProcessorProvider : SymbolProcessorProvider { | ||
| override fun create( | ||
| environment: SymbolProcessorEnvironment | ||
| ): SymbolProcessor { | ||
| return RemoteConfigProcessor(environment.codeGenerator) | ||
| } | ||
| } |
1 change: 1 addition & 0 deletions
1
...in/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| org.wordpress.android.processor.RemoteConfigProcessorProvider |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,8 @@ package org.wordpress.android.processor | |
|
|
||
| import com.tschuchort.compiletesting.KotlinCompilation | ||
| import com.tschuchort.compiletesting.SourceFile | ||
| import com.tschuchort.compiletesting.kspWithCompilation | ||
| import com.tschuchort.compiletesting.symbolProcessorProviders | ||
| import org.assertj.core.api.Assertions.assertThat | ||
| import org.jetbrains.kotlin.utils.addToStdlib.cast | ||
| import org.junit.Test | ||
|
|
@@ -32,12 +34,7 @@ class RemoteConfigProcessorTest { | |
| ) | ||
|
|
||
| // when | ||
| val result = compile( | ||
| listOf( | ||
| remoteFieldA, | ||
| featureA, /* adding a feature, as without it, annotation processor won't start */ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice that we are able to fix this 😄 |
||
| ) | ||
| ) | ||
| val result = compile(listOf(remoteFieldA)) | ||
|
|
||
| // then | ||
| assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK) | ||
|
|
@@ -102,12 +99,7 @@ class RemoteConfigProcessorTest { | |
| ) | ||
|
|
||
| // when | ||
| val result = compile( | ||
| listOf( | ||
| experiment, | ||
| featureA, /* adding a feature, as without it, annotation processor won't start */ | ||
| ) | ||
| ) | ||
| val result = compile(listOf(experiment)) | ||
|
|
||
| // then | ||
|
|
||
|
|
@@ -124,7 +116,8 @@ class RemoteConfigProcessorTest { | |
|
|
||
| private fun compile(src: List<SourceFile>) = KotlinCompilation().apply { | ||
| sources = src + fakeAppConfig | ||
| annotationProcessors = listOf(RemoteConfigProcessor()) | ||
| symbolProcessorProviders = listOf(RemoteConfigProcessorProvider()) | ||
| kspWithCompilation = true | ||
| inheritClassPath = true | ||
| messageOutputStream = System.out | ||
| }.compile() | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All output files are set to
aggregatingstrategy for incremental processing. They have to, as we gather annotations from all available files in the codebase.See https://kotlinlang.org/docs/ksp-incremental.html#aggregating-vs-isolating for more details.