https://kotlinlang.org logo
#ksp
Title
# ksp
j

jean

08/12/2022, 9:24 AM
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

Grégory Lureau

08/12/2022, 10:01 AM
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

jean

08/12/2022, 11:41 AM
Do you have an example of how you do it?
g

Grégory Lureau

08/12/2022, 11:47 AM
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

jean

08/12/2022, 12:09 PM
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

Grégory Lureau

08/12/2022, 12:16 PM
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

Jiaxiang

08/12/2022, 5:57 PM
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

jean

08/13/2022, 7:48 AM
Thanks for all the help!
h

Hristijan

10/16/2023, 11:06 PM
@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

Jiaxiang

10/23/2023, 9:52 PM
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

Hristijan

10/24/2023, 12:54 PM
@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

jean

10/24/2023, 3:33 PM
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

Jiaxiang

10/25/2023, 4:56 PM
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

Hristijan

10/26/2023, 7:25 AM
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?
3 Views