Skip to content

Commit 5490e4e

Browse files
committed
refactor: rewrite RemoteConfigProcessor to ksp
1 parent 53a4dd0 commit 5490e4e

File tree

4 files changed

+109
-110
lines changed

4 files changed

+109
-110
lines changed

WordPress/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ dependencies {
346346
implementation 'androidx.webkit:webkit:1.10.0'
347347
implementation "androidx.navigation:navigation-compose:$androidxComposeNavigationVersion"
348348
compileOnly project(path: ':libs:annotations')
349-
kapt project(':libs:processors')
349+
ksp project(':libs:processors')
350350
implementation (project(path:':libs:networking')) {
351351
exclude group: "com.android.volley"
352352
exclude group: 'org.wordpress', module: 'utils'
Lines changed: 94 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,128 +1,113 @@
1+
@file:OptIn(KspExperimental::class)
2+
3+
14
package org.wordpress.android.processor
25

3-
import com.google.auto.service.AutoService
4-
import com.squareup.kotlinpoet.DelicateKotlinPoetApi
5-
import com.squareup.kotlinpoet.TypeName
6-
import com.squareup.kotlinpoet.asTypeName
6+
import com.google.devtools.ksp.KspExperimental
7+
import com.google.devtools.ksp.getAnnotationsByType
8+
import com.google.devtools.ksp.processing.CodeGenerator
9+
import com.google.devtools.ksp.processing.Resolver
10+
import com.google.devtools.ksp.processing.SymbolProcessor
11+
import com.google.devtools.ksp.symbol.KSAnnotated
12+
import com.google.devtools.ksp.symbol.KSClassDeclaration
13+
import com.squareup.kotlinpoet.ksp.toTypeName
14+
import com.squareup.kotlinpoet.ksp.writeTo
715
import org.wordpress.android.annotation.Experiment
816
import org.wordpress.android.annotation.Feature
9-
import org.wordpress.android.annotation.FeatureInDevelopment
1017
import org.wordpress.android.annotation.RemoteFieldDefaultGenerater
11-
import java.io.File
12-
import javax.annotation.processing.AbstractProcessor
13-
import javax.annotation.processing.Processor
14-
import javax.annotation.processing.RoundEnvironment
15-
import javax.annotation.processing.SupportedAnnotationTypes
16-
import javax.annotation.processing.SupportedSourceVersion
17-
import javax.lang.model.SourceVersion
18-
import javax.lang.model.element.TypeElement
19-
import javax.tools.Diagnostic.Kind
20-
21-
@AutoService(Processor::class) // For registering the service
22-
@SupportedSourceVersion(SourceVersion.RELEASE_8) // to support Java 8
23-
@SupportedAnnotationTypes(
24-
"org.wordpress.android.annotation.Experiment",
25-
"org.wordpress.android.annotation.Feature",
26-
"org.wordpress.android.annotation.FeatureInDevelopment",
27-
"org.wordpress.android.annotation.RemoteFieldDefaultGenerater"
28-
)
29-
class RemoteConfigProcessor : AbstractProcessor() {
30-
@OptIn(DelicateKotlinPoetApi::class)
31-
@Suppress("DEPRECATION")
32-
override fun process(p0: MutableSet<out TypeElement>?, roundEnvironment: RoundEnvironment?): Boolean {
33-
val experiments = roundEnvironment?.getElementsAnnotatedWith(Experiment::class.java)?.map { element ->
34-
val annotation = element.getAnnotation(Experiment::class.java)
35-
annotation.remoteField to annotation.defaultVariant
36-
} ?: listOf()
37-
val remoteFeatureNames = mutableListOf<TypeName>()
38-
val features = roundEnvironment?.getElementsAnnotatedWith(Feature::class.java)?.map { element ->
39-
val annotation = element.getAnnotation(Feature::class.java)
40-
remoteFeatureNames.add(element.asType().asTypeName())
41-
annotation.remoteField to annotation.defaultValue.toString()
42-
} ?: listOf()
43-
val remoteFields = roundEnvironment?.getElementsAnnotatedWith(RemoteFieldDefaultGenerater::class.java)
44-
?.map { element ->
45-
val annotation = element.getAnnotation(RemoteFieldDefaultGenerater::class.java)
46-
annotation.remoteField to annotation.defaultValue
47-
} ?: listOf()
48-
val featuresInDevelopment = roundEnvironment?.getElementsAnnotatedWith(FeatureInDevelopment::class.java)
49-
?.map { element ->
50-
element.asType().toString()
51-
} ?: listOf()
52-
return if (experiments.isNotEmpty() || features.isNotEmpty()) {
53-
generateRemoteFieldConfigDefaults(remoteFields.toMap())
54-
generateRemoteFeatureConfigDefaults((experiments + features).toMap())
55-
generateRemoteFeatureConfigCheck(remoteFeatureNames)
56-
generateFeaturesInDevelopment(featuresInDevelopment)
57-
true
58-
} else {
59-
false
60-
}
61-
}
6218

63-
@Suppress("TooGenericExceptionCaught", "SwallowedException")
64-
private fun generateRemoteFeatureConfigDefaults(
65-
remoteConfigDefaults: Map<String, String>
66-
) {
67-
try {
68-
val fileContent = RemoteFeatureConfigDefaultsBuilder(remoteConfigDefaults).getContent()
69-
70-
val kaptKotlinGeneratedDir = processingEnv.options[KAPT_KOTLIN_GENERATED_OPTION_NAME]
71-
fileContent.writeTo(File(kaptKotlinGeneratedDir))
72-
} catch (e: Exception) {
73-
processingEnv.messager.printMessage(Kind.ERROR, "Failed to generate remote feature config defaults")
19+
@OptIn(KspExperimental::class)
20+
class RemoteConfigProcessor(
21+
private val codeGenerator: CodeGenerator,
22+
) : SymbolProcessor {
23+
/**
24+
* See: https://github.com/google/ksp/issues/797#issuecomment-1041127747
25+
* Also: https://github.com/google/ksp/blob/a0cd7774a7f65cec45a50ecc8960ef5e4d47fc21/examples/playground/test-processor/src/main/kotlin/TestProcessor.kt#L20
26+
*/
27+
private var invoked = false
28+
29+
override fun process(resolver: Resolver): List<KSAnnotated> {
30+
if (invoked) {
31+
return emptyList()
7432
}
33+
34+
val remoteFeatures = resolver.getSymbolsWithAnnotation("org.wordpress.android.annotation.Feature")
35+
.toList()
36+
37+
generateRemoteFeatureConfigDefaults(resolver, remoteFeatures)
38+
generateRemoteFieldsConfigDefaults(resolver)
39+
generateFeaturesInDevelopment(resolver)
40+
generateRemoteFeatureConfigCheck(remoteFeatures)
41+
42+
invoked = true
43+
return emptyList()
7544
}
7645

77-
@Suppress("TooGenericExceptionCaught", "SwallowedException")
78-
private fun generateRemoteFieldConfigDefaults(
79-
remoteConfigDefaults: Map<String, String>
80-
) {
81-
try {
82-
val fileContent = RemoteFieldConfigDefaultsBuilder(remoteConfigDefaults).getContent()
83-
84-
val kaptKotlinGeneratedDir = processingEnv.options[KAPT_KOTLIN_GENERATED_OPTION_NAME]
85-
fileContent.writeTo(File(kaptKotlinGeneratedDir))
86-
} catch (e: Exception) {
87-
processingEnv.messager.printMessage(Kind.ERROR, "Failed to generate remote feature config defaults")
88-
}
46+
private fun generateRemoteFeatureConfigDefaults(resolver: Resolver, remoteFeatures: List<KSAnnotated>) {
47+
val experiments = resolver.getSymbolsWithAnnotation("org.wordpress.android.annotation.Experiment")
48+
.toList()
49+
50+
val defaults = (remoteFeatures + experiments)
51+
.map { element: KSAnnotated ->
52+
val featuresDefaults = element.getAnnotationsByType(Feature::class)
53+
.toList().associate { annotation ->
54+
annotation.remoteField to annotation.defaultValue.toString()
55+
}
56+
val experimentsDefaults = element.getAnnotationsByType(Experiment::class).toList()
57+
.toList().associate { annotation ->
58+
annotation.remoteField to annotation.defaultVariant
59+
}
60+
featuresDefaults + experimentsDefaults
61+
}.flatMap { it.toList() }
62+
.toMap()
63+
64+
RemoteFeatureConfigDefaultsBuilder(defaults).getContent()
65+
.writeTo(
66+
codeGenerator,
67+
aggregating = true,
68+
)
8969
}
9070

91-
@Suppress("TooGenericExceptionCaught")
92-
private fun generateRemoteFeatureConfigCheck(
93-
remoteFeatureNames: List<TypeName>
94-
) {
95-
try {
96-
val fileContent = RemoteFeatureConfigCheckBuilder(remoteFeatureNames).getContent()
97-
98-
val kaptKotlinGeneratedDir = processingEnv.options[KAPT_KOTLIN_GENERATED_OPTION_NAME]
99-
fileContent.writeTo(File(kaptKotlinGeneratedDir))
100-
} catch (e: Exception) {
101-
processingEnv.messager.printMessage(
102-
Kind.ERROR,
103-
"Failed to generate remote feature config check: $e"
71+
private fun generateRemoteFieldsConfigDefaults(resolver: Resolver) {
72+
val remoteFieldDefaults =
73+
resolver.getSymbolsWithAnnotation("org.wordpress.android.annotation.RemoteFieldDefaultGenerater")
74+
.toList()
75+
.associate { element: KSAnnotated ->
76+
element.getAnnotationsByType(RemoteFieldDefaultGenerater::class)
77+
.toList()
78+
.first()
79+
.let { annotation ->
80+
annotation.remoteField to annotation.defaultValue
81+
}
82+
}
83+
84+
RemoteFieldConfigDefaultsBuilder(remoteFieldDefaults).getContent()
85+
.writeTo(
86+
codeGenerator,
87+
aggregating = true,
10488
)
105-
}
10689
}
10790

108-
@Suppress("TooGenericExceptionCaught")
109-
private fun generateFeaturesInDevelopment(
110-
remoteFeatureNames: List<String>
111-
) {
112-
try {
113-
val fileContent = FeaturesInDevelopmentDefaultsBuilder(remoteFeatureNames).getContent()
114-
115-
val kaptKotlinGeneratedDir = processingEnv.options[KAPT_KOTLIN_GENERATED_OPTION_NAME]
116-
fileContent.writeTo(File(kaptKotlinGeneratedDir))
117-
} catch (e: Exception) {
118-
processingEnv.messager.printMessage(
119-
Kind.ERROR,
120-
"Failed to generate remote config check: $e"
91+
private fun generateFeaturesInDevelopment(resolver: Resolver) {
92+
val featuresInDevelopmentDefaults =
93+
resolver.getSymbolsWithAnnotation("org.wordpress.android.annotation.FeatureInDevelopment")
94+
.filterIsInstance<KSClassDeclaration>()
95+
.toList()
96+
.map { it.simpleName.asString() }
97+
98+
FeaturesInDevelopmentDefaultsBuilder(featuresInDevelopmentDefaults).getContent()
99+
.writeTo(
100+
codeGenerator,
101+
aggregating = true,
121102
)
122-
}
123103
}
124104

125-
companion object {
126-
const val KAPT_KOTLIN_GENERATED_OPTION_NAME = "kapt.kotlin.generated"
105+
private fun generateRemoteFeatureConfigCheck(remoteFeatures: List<KSAnnotated>) {
106+
RemoteFeatureConfigCheckBuilder(
107+
remoteFeatures.filterIsInstance<KSClassDeclaration>().map { it.asType(emptyList()).toTypeName() }
108+
).getContent().writeTo(
109+
codeGenerator,
110+
aggregating = true,
111+
)
127112
}
128113
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package org.wordpress.android.processor
2+
3+
import com.google.devtools.ksp.processing.SymbolProcessor
4+
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
5+
import com.google.devtools.ksp.processing.SymbolProcessorProvider
6+
7+
class RemoteConfigProcessorProvider : SymbolProcessorProvider {
8+
override fun create(
9+
environment: SymbolProcessorEnvironment
10+
): SymbolProcessor {
11+
return RemoteConfigProcessor(environment.codeGenerator)
12+
}
13+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
org.wordpress.android.processor.RemoteConfigProcessorProvider

0 commit comments

Comments
 (0)