How should I proceed to get all the properties fro...
# ksp
j
How should I proceed to get all the properties from all the classes annotated with a given annotation and create an enum out of them? The example I followed so far deals with one annotated class at a time, is it possible to create a file when processing the first file and add values to it in the following classes?
g
I use a visitor that aggregates all the data I need, then after the visit I use the aggregated data and then generate what I need. Documentation available here about the aggregation
j
Do you have an example of how you do it?
g
Copy code
val aggVisitor = AggregatorVisitor(resolver)
val annotated = resolver.getSymbolsWithAnnotation(MyAnnotation::class.qualifiedName!!)
annotated.forEach { it.accept(aggVisitor, Unit) }
then you generate your code, and finally
Copy code
environment.codeGenerator.createNewFile(
        dependencies = Dependencies(aggregating = true, *aggVisitor.sources), // Here you must put all the sources aggregated
        packageName = packageName,
        fileName = name
    ).use { outputStream ->
        //...
    }
(It's usually better to use isolation for build efficiency, so I tend to avoid that as much as possible)
j
My issue resides in the “then you generate your code” part. Am I supposed to generate partial enums for each annotated class and build a complete one after each class has been processed? My case is like that:
Copy code
@MyAnnotation
data class ClassOne(val a: String, val b: String, val c: String)

@MyAnnotation
data class ClassOne(val b: String, val c: String, val d: String)

-- generated code
enum class MyEnum { A, B, C, D }
I managed to create a separate enum for each data class, but can’t figure out how to combine them or if I can create a complete one directly
g
Visitor implementation can aggregate a representation that will be used to generate your final enum I think... Of I get it, I was thinking inside the same compilation module, are you dealing with multi-modules?
If it's mono-module, something like that :
Copy code
data class MyEnum(val enumName: String, val enumEntries: List<String>)
class AggVisitor : KSVisitorVoid() {
  val enums = mutableListOf<MyEnum>()
  override visitClass(...) {
    enums.add(MyEnum(...))
  }
}
with that, you can generate 1 enum at the end of the (module) compilation. If it's multi-module it's not easily feasible afaik.
j
In a single round case it shouldn’t be an issue as you can just store the properties in a collection and generate your code accordingly. For a multiple round case it might be a little bit tricky, there are a couple of possible scenarios, you should be fine to use a cross round collection to store the properties, just make that collection a member of your processor and initialize it before processing. You need to be careful on when you want to consume that stored properties and generate code, though, as KSP does not support modifying existing code, once you have generated, you can’t modify it.
j
Thanks for all the help!
h
@Jiaxiang sorry for the late reply/spam, i’m having the same use case, I want to generated an aggregator that brings together a list of classes but it needs to wait for the latest processor (multi module project) to bring them together into one, is there a sample code how this might be achieved? For example I want to achieve this app module: has
object GraphFactory { val list = listOf<Graph>(
module A: appends
MyGraphA,
module B: appends
MyGraphB,
module C (last module): appends
MyGraphC) }
@jean can you please maybe share your solution?
j
Hi @Hristijan, your case is quite complicated as it involves different modules, for such case, can I assume
app module
will be compiled last since compiling it before module A B C makes no sense due to not yet exist
MyGraph
classes? In that case when you run KSP on app module, the corresponding MyGraph A B C should have been generated and you should be able to get them? The tricky part in this case is that you can’t get the generated
MyGraph A B C
easily from KSP’s get symbols with annotation utils since these MyGraph classes are not part of the source from app module’s point of view.
h
@Jiaxiang yes the app module will be the last one that's gonna be compiled But i have no clue how to check whether that GraphFactory was already created so that I can append to it, how do I check this?
j
I actually ended up with another approach altogether, what I was trying to do wasn't answering my needs. I can't help much here, sorry.
j
maybe you can try to look for the class declaration of
GraphFactory
and if you can get it via
getClassDeclarationByName
then it is created, does it work for you?
h
I'm not sure if this is the correct approach but I did the following When the app module is built i use
getDeclarationsFromPackage
since I'm using a common package for the generated code classes and that's how I aggregate them, I guess it doesn't differ much from your mentioned approach since yours is directly accessing the classDeclarationBy the name?