-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: rewrite
RemoteConfigProcessor
to ksp
- Loading branch information
Showing
4 changed files
with
109 additions
and
110 deletions.
There are no files selected for viewing
This file contains 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
203 changes: 94 additions & 109 deletions
203
libs/processors/src/main/java/org/wordpress/android/processor/RemoteConfigProcessor.kt
This file contains 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,113 @@ | ||
@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.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 | ||
} | ||
} | ||
|
||
@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") | ||
@OptIn(KspExperimental::class) | ||
class RemoteConfigProcessor( | ||
private val codeGenerator: CodeGenerator, | ||
) : SymbolProcessor { | ||
/** | ||
* 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 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 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() | ||
|
||
RemoteFeatureConfigDefaultsBuilder(defaults).getContent() | ||
.writeTo( | ||
codeGenerator, | ||
aggregating = true, | ||
) | ||
} | ||
|
||
@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 generateRemoteFieldsConfigDefaults(resolver: Resolver) { | ||
val remoteFieldDefaults = | ||
resolver.getSymbolsWithAnnotation("org.wordpress.android.annotation.RemoteFieldDefaultGenerater") | ||
.toList() | ||
.associate { element: KSAnnotated -> | ||
element.getAnnotationsByType(RemoteFieldDefaultGenerater::class) | ||
.toList() | ||
.first() | ||
.let { annotation -> | ||
annotation.remoteField to annotation.defaultValue | ||
} | ||
} | ||
|
||
RemoteFieldConfigDefaultsBuilder(remoteFieldDefaults).getContent() | ||
.writeTo( | ||
codeGenerator, | ||
aggregating = true, | ||
) | ||
} | ||
} | ||
|
||
@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 generateFeaturesInDevelopment(resolver: Resolver) { | ||
val featuresInDevelopmentDefaults = | ||
resolver.getSymbolsWithAnnotation("org.wordpress.android.annotation.FeatureInDevelopment") | ||
.filterIsInstance<KSClassDeclaration>() | ||
.toList() | ||
.map { it.simpleName.asString() } | ||
|
||
FeaturesInDevelopmentDefaultsBuilder(featuresInDevelopmentDefaults).getContent() | ||
.writeTo( | ||
codeGenerator, | ||
aggregating = true, | ||
) | ||
} | ||
} | ||
|
||
companion object { | ||
const val KAPT_KOTLIN_GENERATED_OPTION_NAME = "kapt.kotlin.generated" | ||
private fun generateRemoteFeatureConfigCheck(remoteFeatures: List<KSAnnotated>) { | ||
RemoteFeatureConfigCheckBuilder( | ||
remoteFeatures.filterIsInstance<KSClassDeclaration>().map { it.asType(emptyList()).toTypeName() } | ||
).getContent().writeTo( | ||
codeGenerator, | ||
aggregating = true, | ||
) | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
...processors/src/main/java/org/wordpress/android/processor/RemoteConfigProcessorProvider.kt
This file contains 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 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 |