I’m trying to use a code generation library from my Gradle build script, but i’m facing an error - I don’t seem to be able to access my own classes from the build file. The task I have is the following:
def exporter = new GenericExporter();
exporter.setTargetFolder(sourceSets.generated.java.srcDirs.first());
exporter.export("com.company.project");
The default GenericExporter constructor is instantiated with a Thread.currentThread().getContextClassLoader() class loader. When I run the task, I get nothing, meaning no classes that are in the com.company.project are found.
When I have the same code in my Application class (the main class inside my project), the code generation works. I debugged and saw that when executing the task from the gradle file I don’t have access to my own com.company.project classes.
Moreover, the project has a multi-project setup. The code generation task I’ve added to subprojects. Moreover I researched and tried to also add the following inside subprojects, but it didn’t help:
This is the problem in that the build script cannot depend on code that is compiled by the build script. There is a cyclic dependency here. You have a couple of options.
Put the code needed by the build in the buildSrc project
Publish the code needed by the build and depend on it as an external dependency
Hi, I read about that problem but seem to not be able to grasp it completely. Wouldn’t having a task that first compiles only the model classes and then one that generates other files from the already compiled models be sufficient?
I’m saying that because both of your solutions seem to suggest kind of major changes - either a buildSrc project that contains the current top level project with all subproject or 2) (again having a dependency on the whole project). I’m looking for a simple solution that would involve minimum changes to the current build setup.
I think you and Mark misunderstood each other. You don’t want to use the compiled classes in the buildscript. You want to postprocess them in another task. That’s an important difference.
That sounds too inflexible, the exporter should take a ClassLoader argument.
Of course, the buildscript is compiled and instantiated before your build can start, so your production code cannot be in the same classloader.
If you want to load your production classes for further processing, you can use a URLClassLoader. You might want to change your exporter to use a more efficient library like ASM though. That’s much faster.
I assumed there might be some misunderstanding, but I couldn’t express myself more clearly.
Yes, it does in fact does take a class loader as argument.
The exporter is not mine to change, it’s part of the QueryDSL library.
I tried to do so, but again with no result. Maybe this is a good time to mention that the files I’m trying to process are Groovy ones. Nevertheless, I tried to instantiate the class loader by two different ways, as shown below, but printing the loaded classes inside of both reveals not a single “com.company.project” class (only the jars of some external libraries I depend on).
def classLoader = new URLClassLoader(sourceSets.main.compileClasspath.collect { it.toURI().toURL() } as URL[])
def classLoader = new URLClassLoader(configurations.runtime.collect { it.toURI().toURL() } as URL[])
Thank you, it’s currently working with the sourceSets.main.runtimeClasspath. The problem I had before when I tried to use the runtimeClasspath was that I received an error saying that I cannot change dependencies of configuration ':app:runtime' after it has been resolved.
I “fixed” this by adding my generation task in another subproject section below the last project definition, i.e. after project('n'). This works, but 1) looks a bit strange and it would be great to have the previous structure and 2) it seems to be executed on every other task that I execute, i.e. compileGroovy, clean, etc.
Again, thanks for the very useful responses
You should only be instantiating the URLClassLoader inside the task action, not in the configuration phase. Otherwise, you are resolving dependencies too early.