Why is the project.sourceSets.main.runtimeClasspath always blank?


(Abhijit Sarkar) #1

I’m writing a plugin to generate Java code from WSDL. Problem is, my task does not find the Java class I’m trying to execute and blows up at runtime with a ClassNotFoundException even though the necessary jar is listed as a compile dependency. I’m using project.sourceSets.main.runtimeClasspath but have tried compileClasspath, adding a build script section to the build file, using configurations.runtime, all to no avail. Note that my project has no Java src code, just Groovy.

Any ideas? The task, a unit test and the build file can be found here: https://gist.github.com/abhijitsarkar/8432347


(Peter Niederwieser) #2

It would be better to pass the class name as a String. Anyway, even though I can’t say for sure what’s causing the problem, what I can say is that ‘ProjectBuilder’ is only meant to be used for unit tests, but not for integration tests that execute tasks. For the latter, the Gradle tooling API should be used. Also, it isn’t safe to call ‘task.execute()’. Only Gradle may call this method.


(Abhijit Sarkar) #3

Even if I didn’t execute the task, the classpath is already blank in the test constructor. Also, is there a lifycycle method that I can override to configure params instead of using configure(Closure c) which may not get called depending on the configuration? Something that gets executed after the constructor and properties set?


(Peter Niederwieser) #4

Even if I didn’t execute the task, the classpath is already blank in the test constructor.

That’s because no dependencies are defined. Also, I doubt that dependency resolution would work when using ‘ProjectBuilder’ (which is just meant for unit tests). You’ll have to use the tooling API, or test manually using a real build.

Also, is there a lifycycle method […]

Not sure what you are trying to achieve here. Why don’t you call ‘initWsdlUrls()’ at the beginning of the task action?


(Abhijit Sarkar) #5

Not sure I understand the question.

I override configure(Closure c) to do stuff I need to do after properties are set. However, it is my understanding that if the task is not configured with a closure (by whoever configuring it, in this case the plugin), configure(Closure c) is not going to be called. Is there another method I can use to initialize internal params after the client has configured the task?


(Peter Niederwieser) #6

Overriding ‘configure’ is not a good solution. You can simply do the initialization in the task action.


(Abhijit Sarkar) #7

That’s because no dependencies are defined.

Compile dependencies are defined. I’ve a client code to test the plugin too, throws the same ClassNotFoundException.


(Peter Niederwieser) #8

Not in the code you showed. That’s all I can say.


(Abhijit Sarkar) #9

We must not be referring to the same thing because the following is copied directly from the gist:

dependencies {
 compile(
   gradleApi(),
   [group: "org.apache.cxf", name: "cxf-tools-wsdlto-core", version: cxfVersion],
   )
 testCompile group:"junit", name:"junit", version:"4.11"
}

(Peter Niederwieser) #10

These are the dependencies for the plugin build, not the dependencies for the ‘project’ used by the test. The test is essentially performing a build within the build, which is independent from the “outer” build. To make this work reliably, the test would also have to use the tooling API.


(Abhijit Sarkar) #11

I couldn’t find any examples of tooling API actually running a task in the same project in the samples. Of course, if a plugin is already applied to a build file, it’s easy to run some task by name. When the task exists in the same project, how does tooling API run it? Using forTasks with the class name or class object throws an exception.


(Peter Niederwieser) #12

Sorry, I don’t understand the question. The tooling API allows to run any valid Gradle build, just like you would run it from the command line.


(Abhijit Sarkar) #13

What I’m saying is that the following doesn’t work because the task name isn’t defined yet. Neither does using the class like “WsImportTask” works in place of the name.

connection.newBuild().forTasks("wsimport").run();

(Peter Niederwieser) #14

Does the build that you are running via the tooling API have a build script that declares a task named “wsimport”? If not, that’s what you’ll have to do.


(Abhijit Sarkar) #15

The wsimport task is defined in the same project in a custom plugin (shown in the gist in the OP). Is there a way to declare it using tooling API instead of the build file? Simply put, how would you execute a task that’s defined in the same project? Sorry of this is obvious, I’m new to Gradle and the sample custom plugin unit tests are trivially simple and not very helpful.


(Peter Niederwieser) #16

It’s important to understand that the build for the plugin, and the build executed by your test (via the tooling API), are completely separate things. The tooling API is simply a programmatic way to interact with Gradle, just like the command line interface is a way to interact with Gradle from the keyboard. In other words, you’ll have to write a separate ‘build.gradle’ for your test build. I recommend you check out the tooling API samples in the full Gradle distribution.

Writing plugin tests using the tooling API takes some effort. In particular, you’ll have to make your plugin available to test builds. You may want to start out by manually testing your plugin.


(Abhijit Sarkar) #17

I ended up rewriting the plugin using convention mapping. Thanks for your help though.