In my previous post I promised to describe how to compile Groovy and Java together without writing complicated ant scripts or limiting yourself in design decisions.
Let us analyze the problem and probably it will lead us to a solution:
- To compile Java code, which refers some Groovy code, we have to compile Groovy first because the only way for javac to know about Groovy is .class file
- We can’t compile Groovy code if it has to know about Java one because again the only way for groovyc to know about Java code is .class file form.
- But do we really need as much? Obviously, not. We don’t really need compiled Groovy classes during Java compilation. All we need to know are “exported interfaces” of the classes. What are their names, methods, fields, implemented interfaces, superclass etc. We don’t care about real implementation at all. Let me repeat it is once more. We don’t need it at all.
- So if we are able to supply javac with .class files with the same “exported interfaces” as our Groovy classes we are done. After that we are able to compile Java code and provide compiled code to groovyc. And our circular dependencies will be resolved.
- It is cool, isn’t it? All we need to do is to write simple compiler which will take Groovy code and create .class file with the same “exported interface” as our Groovy code but ignoring all implementation logic, possible errors etc.
- But how does it help us with “ant script problem”. Unfortunately, it doesn’t because we need to call this special compiler.
- Wait a minute. Why do we need additional compiler? We can build it in groovyc itself. It will be one task to call for joint compilation of Groovy and Java code, which will do three passes internally. First pass will generate dummy .class files, usually such things called stubs. The second one will call regular Java compiler, for example javac or eclipse compiler if we like. And the final step will do usual Groovy compilation. And it is exactly what we need, isn’t it? Yes, it is exactly what we were looking for.
- So the only problem we have is to patch groovyc. We know how to parse and even compile Groovy sources. We know how to call external compiler. We have great ASM library, which will help us to generate stubs. We just need to put it all together. Not too complicated. Right?
- OK, there is one more problem, which still worry me. I know that ASM is great library but I have no too much experience with it and it may take long time to code and especially debug this code. Sounds like a problem, right? Really not at all! We don’t need compiled classes. What we need is to provide javac with information about “exported interfaces” of our Groovy code. It can be .class files or … Java source. Let us just generate Java sources with exactly the same “exported interfaces” as our Groovy code. It is really easy task if you have parser for Groovy, which we obviously have, and what can be easier than such generator? “Hell, World!” probably.
- We have last thing to improve. Currently we plan to parse Groovy code twice – once for stubs generation and once more during Groovy compilation. It is not dramatically bad but unpleasant. Fortunately, it is easy to resolve. What we need is to incorporate our patch in the middle of Groovy compilation process. Parse Groovy, generate stubs, compile java and stubs and continue Groovy compilation. What is even more fortunately is that Groovy compiler is modular enough to allow it.
What’s it. We found solution for all our problems and our quest completed. There is interesting story about different technical details and difficulties, which I met implementing this approach, but I will describe it in the later posts.
And wait a second. The patch gonna be incorporated in to Groovy trunk in nearest days.