Hi! I’m exploring the possibilities of generators ...
# ksp
d
Hi! I’m exploring the possibilities of generators depending on each other. In my case, the dependency is not expressed well with the code generated by one generator, and the other one waiting for code to appear. So, instead I maintain a certain “model” which multiple generators can read and update during their resolve phases, in addition to reading and outputting code, of course. So far I share that model between generators simply by using static data, basically companion object. As far as I can tell, in API there is currently no notion of common context of the current ksp invocation which could be used for storing shared data. What would you say, does using static data brings some risks of things going wrong? Maybe there is other means to such problem, or maybe plans?
e
Would recommend leveraging multiple rounds. Have the first generator generate annotations the second one consumes.
💯 1
👍 1
d
Well, yes, it’s clear that it’s the preferred way. But as I said,
In my case, the dependency is not expressed well with the code generated by one generator, and the other one waiting for code to appear.
m
There is no guarantee that I know of that the next rounds will run in the same JVM etc, so no guarantee that your static variables will still be there. Also no guarantee that a JVM will not be re-used, so if you are running multiple compilations, there is no guarantee that you won't have data left over from previous compilations. I think this doesn't align with the life cycle of a KSP processor. Unless the model you are generating is very computationally expensive, why not create an extension function or something and generate it again? Seems better to waste a few CPU cycles instead of going against the lifecycle design.
d
There is no guarantee that I know of that the next rounds will run in the same JVM etc
I think there is. Seems that SymbolProcessor is guaranteed to be the same instance from round to round (to be able to maintain the state), so it should be in the same JVM.
Also no guarantee that a JVM will not be re-used
That’s a good catch, yes. I’m also thinking of a potential risk of gradle parallel build. However, speaking of recomputing the model argument, I would throw in another reason for me sharing the state between generators, for completeness. In my case, input for generators is a hierarchy of interfaces. Say, gen. A uses it to output something. But gen. B adds new interfaces to the hierarchy. I want to make sure A runs after B, so that it uses all interfaces including new ones. And, as a complication I might add more consuming generators and more producing generators later. And I don’t want to couple them tightly, so that all consumers know about all potential producers and wait for a specific “tag” in the generated sources to appear which would signal that now it’s ok to run the consumers. I want the code for a consumer generator to be unaware of how many potential producers/modifiers of the hierarchy there are. Back to technical nuances of using static data, seems it would be enough to have an object (just its identity is enough) which uniquely identifies the current KSP run (
AbstractKotlinSymbolProcessingExtension.doAnalysys()
) Then, it could be used as a key to some static map. As a matter of fact, now CodeGenerator and Resolver instances are already such objects (they are unique and stable for the given run), but there are definitely no guarantees to that. So, relying on that would be a hack
m
Could you create your own annotation to use for storing that.You can then add a string value to it. You could put json in the string. e.g. @MyAnnotation(value = "state: { ... } ") Then on the next round - lookup MyAnnotation If you use AnnotationRetention.Source on your annotation, the annotation won't even be stored or wasting space in the compiled output.
d
Ok… That sounds like an interesting idea, thanks! Actually, yes, I’m starting to incline towards trying to achieve the ordering requirement strictly with KSP output/input. As for recomputing the model, you’re probably right there as well. However still, in the situation when there are multiple generators of the same model, and each can run multiple rounds, it seems attractive to be able to cache the model computation (e.g. with the set of declarations used as key in the cache). Actually, if that cache would be static data, in my view that would be the most “innocent” use of static data. Because, absence in the cache would not break anything and because concrete declarations would be used as key. However, that introduces the question of equality between KSDeclaration instances which is not quite clear to me.
(oh, btw: a given generator already now can cache anything between rounds, in its own instance variables, so that’s out of the question)
👍 1
m
I would definitely try and make sure that you don't rely on any behavior that isn't documented, because it could break at anytime.