Gradle EclipseProject and BuildInvocations model for the same project have different tasks for projects

Suppose I have the following project structure in Eclipse:

For example, I got a sample project, which got all project folders in the same directory (including root project).

{noformat} workspace – project1 – project2 – rootProject {noformat}

rootProject got a settings.gradle file with the following content: {code:title=settings.gradle} includeFlat ‘project1’ includeFlat ‘project2’ {code}

The build.gradle located in the rootProject contains the following: {code:title=build.gradle} subprojects {

task hello << {

println “Hello from “ + project.name

} } {code}

If I build the EclipseProject models for these projects I’d get “hello” task on project1 and on project2 If I build the BuildInvocations models for these projects I’d get only “hello” task selector on the root project.

If I use Gradle Tooling API to launch tasks, namely: org.gradle.tooling.BuildLauncher.forTasks(String… tasks) BuildLauncher is created based on the created ProjectConnection for one of these projects.

Created BuildLauncher for the root project and task argument is “hello” - executes as expected Here is the output :my-lib:hello Hello from my-lib :product:hello Hello from product

BUILD SUCCESSFUL

Total time: 0.108 secs

If I attempt to execute “hello” or “:hello” on the BuildLauncher created based on ProjectConnection to “project1” I get an exception during the build: Caused by: org.gradle.execution.TaskSelectionException: Task ‘hello’ not found in root project ‘project1’. Some candidates are: ‘help’.

at org.gradle.execution.TaskSelector.getSelection(TaskSelector.java:71)

at org.gradle.execution.TaskSelector.getSelection(TaskSelector.java:52)

at org.gradle.execution.commandline.CommandLineTaskParser.parseTasks(CommandLineTaskParser.java:42)

at org.gradle.execution.TaskNameResolvingBuildConfigurationAction.configure(TaskNameResolvingBuildConfigurationAction.java:44)

at org.gradle.execution.DefaultBuildExecuter.configure(DefaultBuildExecuter.java:42)

at org.gradle.execution.DefaultBuildExecuter.access$100(DefaultBuildExecuter.java:23)

at org.gradle.execution.DefaultBuildExecuter$1.proceed(DefaultBuildExecuter.java:48)

at org.gradle.execution.ExcludedTaskFilteringBuildConfigurationAction.configure(ExcludedTaskFilteringBuildConfigurationAction.java:46)

at org.gradle.execution.DefaultBuildExecuter.configure(DefaultBuildExecuter.java:42)

at org.gradle.execution.DefaultBuildExecuter.access$100(DefaultBuildExecuter.java:23)

at org.gradle.execution.DefaultBuildExecuter$1.proceed(DefaultBuildExecuter.java:48)

at org.gradle.execution.DefaultTasksBuildExecutionAction.configure(DefaultTasksBuildExecutionAction.java:40)

at org.gradle.execution.DefaultBuildExecuter.configure(DefaultBuildExecuter.java:42)

at org.gradle.execution.DefaultBuildExecuter.select(DefaultBuildExecuter.java:35)

at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:142)

at org.gradle.initialization.DefaultGradleLauncher.doBuild(DefaultGradleLauncher.java:106)

If I try “:project1:hello” I’d get a build exception informing that “project1” cannot be found in root project “project1” Caused by: org.gradle.execution.taskpath.ProjectFinderByTaskPath$ProjectLookupException: Project ‘project1’ not found in root project ‘project1’.

at org.gradle.execution.taskpath.ProjectFinderByTaskPath.findProject(ProjectFinderByTaskPath.java:47)

at org.gradle.execution.taskpath.TaskPathResolver.resolvePath(TaskPathResolver.java:49)

at org.gradle.execution.TaskSelector.getSelection(TaskSelector.java:56)

at org.gradle.execution.TaskSelector.getSelection(TaskSelector.java:52)

at org.gradle.execution.commandline.CommandLineTaskParser.parseTasks(CommandLineTaskParser.java:42)

at org.gradle.execution.TaskNameResolvingBuildConfigurationAction.configure(TaskNameResolvingBuildConfigurationAction.java:44)

at org.gradle.execution.DefaultBuildExecuter.configure(DefaultBuildExecuter.java:42)

at org.gradle.execution.DefaultBuildExecuter.access$100(DefaultBuildExecuter.java:23)

at org.gradle.execution.DefaultBuildExecuter$1.proceed(DefaultBuildExecuter.java:48)

at org.gradle.execution.ExcludedTaskFilteringBuildConfigurationAction.configure(ExcludedTaskFilteringBuildConfigurationAction.java:46)

at org.gradle.execution.DefaultBuildExecuter.configure(DefaultBuildExecuter.java:42)

at org.gradle.execution.DefaultBuildExecuter.access$100(DefaultBuildExecuter.java:23)

at org.gradle.execution.DefaultBuildExecuter$1.proceed(DefaultBuildExecuter.java:48)

at org.gradle.execution.DefaultTasksBuildExecutionAction.configure(DefaultTasksBuildExecutionAction.java:40)

at org.gradle.execution.DefaultBuildExecuter.configure(DefaultBuildExecuter.java:42)

at org.gradle.execution.DefaultBuildExecuter.select(DefaultBuildExecuter.java:35)

at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:142)

at org.gradle.initialization.DefaultGradleLauncher.doBuild(DefaultGradleLauncher.java:106)

… 47 more

“hello” and “:project1:hello” used to work with Gradle pre 1.12 or pre 1.11

Is task “hello” defined for project1 and project2? We always used EclipseProject and HierarchicalEclipseProject models and just looking into using BuildInvocations model now for Gradle Eclipse 3.6.3. Therefore, previously, we’d show “hello” for project1 and project2 and execute the task as shown above and that was working. Therefore the questions are: 1. Looks like there is a difference between EclipseProject and BuildInvocations model. If yes, which one of them is correct? And when roughly the other model going to be fixed? 2.What’s the recommended way tio launch task “hello”?Is org.gradle.tooling.BuildLauncher.forTasks(String… tasks) still OK to call? Or perhaps it’s best start using forLaunchables(…)?

Thanks in advance.

Cheers, Alex

I think that the problem is related to flat project layout: the build is run from a context of a directory of one of subprojects and doesn’t find settings.gradle file to find that it is part of multi-project build. Using project connection created for a project directory of a root project should help. Or passing --setting-file arg. I think everything done through ToolingAPI will be affected by that if you use connection for project1’s or projects2’s directory.

No ETA for the fix yet and I am not sure if we are able to correctly support this in current state.

forTasks/forLaunchables: we’d like to move towards forLaunchables(). Of course the API is still @Incubating. In the future we expect to do improvements here more than with the forTasks() part.

BTW: is this working well with usual project tree structure? I assume it is.

Yes, it’s working for the non-flat project hierarchy. Similarly defined task “hello” is found on the sub-projects and can be executed with “forTasks” method call with the ProjectConnection object made from the sub-projects. I’ve tried creating ProjectConnection from the root project while executing “:project1:hello” for example and it worked.

It’s just that if we switch to BuildInvocations model in Gradle Eclipse plugin for obtaining tasks (we’d like to do that to show public/internal tasks) for the Gradle Tasks view then we’d lose the “hello” task from this flat layout example in the Gradle Tasks view for subprojects…

You can create connection for root project directory and get BuildInvocations model using BuildAction calling BuildController.getModel(project, BuildInvocations.class). I am afraid it will take a while before we fix tooling API functionality with flat layout.

Thanks Radim! We’ve passed the settings.gradle file with the “-c” parameter when running a model build or executing a task… Passed that command line argument via the withArguments() method to the Gradle’s LongRunningOpeartion. That gave us the ability to execute “hello” task for sub-projects in the flat layout multi-project. Thanks very much for helping out with this!-)

I have another question (might be a silly one) If I have a non-flat multi=project layout: root – project1 – project2

On project1 i have defined the task: task saySomething << {

println “Say something” }

I have built the BuildInvocations model. If I call getTaskSelector() on it i have task “saySomething” in the set If I call getTasks() on it I don’t get task “saySomething” in the set

So, why am I not getting this task with the getTasks() call? I was expecting that getTasks() would return me the tasks defined on the project1 in this case. Task that i could execute with “gradle :project1:” TaskSelectors are launchables that i can execute with "gradle " from the project’s directory Tasks are launchables that i can execute with “gradle ::” from the project’s directory. Is this correct?

Thanks again!

I see. That’s because you get BuildInvocations model for a root project in both cases.

According to

https://github.com/gradle/gradle/blob/04cae76e592d18e5613878261971ea125487c300/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/BuildModelAction.java#L60 and https://github.com/gradle/gradle/blob/139fb29ea242792ebfa7311f71b36ab2f8af9892/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/BuildInvocationsBuilder.java#L70

The tooling model builder for BuildInvocations interface assumes the request is for root project. You can get the model for one of subprojects if you execute a BuildAction and ask BuildController.getModel(Model, BuildInvocations.class) passing the project as model parameter. In retrspective it may not be the best choice but we expected that one project connection is likely to be reused for all operations related to a Gradle build with all its subprojects.

The solution for you will be to use BuildActions. But there is a one big problem: we recently discovered that BuildActions are not working when the code is invoked from Eclipse (more precisely from an app using Equinox where classloaders use bundleresource: protocol rather then file: or jar:). The fix for this is pending and hopefully will fix communication with older tooling providers too.

But there is a one big problem: we recently discovered that BuildActions are not working when the code is invoked from Eclipse (more precisely from an app using Equinox where classloaders use bundleresource: protocol rather then file: or jar:). The fix for this is pending and hopefully will fix communication with older tooling providers too.

Is there a bug for that defect? When will it be fixed?

The BuildActions seems like something we should really be using. I have discovered that we can build ‘little models’ like ProjectPublications for large hierarchies much faster using BuildActions then by building each ‘little model’ for one project at a time. To give you an idea how drastic the difference is:

  • building all ProjectPublications models for all 110 projects in spring-xd without BuildActions takes 110 requests. Each takes upto 4 seconds. This is more than 7 minutes.

  • building all ProjectPublications for the same projects using a single ‘BuildAction’ takes only 6 seconds.

This really is 2 orders of magnitude… and the difference between something that is usable and something that is simply too slow.

BuildAction execution was fixed last week. You can get the fix if you use one of the latest nigthly builds of ToolingAPI.

The difference is expected and the idea is to run BuildAction that will assemble required models and transfer them back in one run.

An update on this: upon further review it was decided not to fix the BuildAction problem for older providers. It means that it is possible to use recent ToolingAPI and execute BuildAction from Eclipse if the provider side uses recent Gradle version (future 2.3). It will fail with older ToolingAPI JAR or older provider. Sorry about that problem. We could and should fix it much sooner if we only knew about it.

Just to make sure I understand what you are saying (as I’m not sure I know what you mean with terms like ‘provider’).

Are you saying that even if we get a fixed version of the tooling API jars into STS then we will still not be able to use BuildActions if a project itself uses and older version of Gradle in its build?

If that is indeed what you are saying then that is going to be a major headache for us. Basically that means we have to choose between a ‘rock and a hard place’:

  • don’t use build actions: extremely slow for building models like ProjectPublications, BuildInvocations etc.

  • use build actions: it only works on latest version of Gradle

Basically, unless you fix it in such a way that it works with older versions of Gradle then it won’t do us any good.

Kris

PS: We don’t mind switching to a newer version of Tooling API, but forcing all our users onto the latest version of Gradle is really not an option.

ToolingAPI has consumer and provider sides. Your application that uses it is on the consumer side and internally ToolingAPI provider is created/instantiated (in your app isolated in its own classloader and it collaborates with running daemon(s)) to serve the requests. The naming here maps to convention used to name classes and packages in Gradle code-base. I admit that it is not quite obvious when someone sees them for the first time.

Indeed it will only bring the benefit in a longer run. Even with the fix you’d have to keep compatibility layer for pre 1.8 Gradle version (when BuildAction was introduced). Of course activating it for pre-2.3 and pre-1.8 is a difference.

Not supporting pre-1.8 may be an acceptable compromise.

Let me rephrase my question really simple. As I’m still not yet sure I understand it.

So if we (STS) used tooling-api 2.3 buildActions, what versions of Gradle are our users limited to? Is it >=1.8 or is it >=2.3 ?

=2.3. Support for older versions need to be done without BuildAction. Or you would have to find a way how to make it working from Eclipse.

Okay, I guess that means we can simply not use build actions until 90% of people have moved on to 2.3. I hope you reconsider your decision not to fix support for older tooling providers.

=2.3. Support for older versions need to be done without BuildAction. Or you would have to find a way how to make it working from Eclipse.

Let’s say, hypothetically that we’d want to do that, how does a tooling client decide whether the build actions can be used? Is there a way to ask the tooling API what version it is using as the provider?

DefaultToolingImplementationLoader creates a classloader for provider classes and loads ConsumerConnection class in it. Depending on capability of this ConsumerConnection it wraps it with into a consumer connection that knows if actions can be used or not.

Now the problem is that provider wants to pass information about BuildAction class to daemon and expected that it is loaded using URL classloader using file: or jar: URL. This is not the case in Eclipse. The fix is in https://github.com/gradle/gradle/commit/67146868751635dc8a4098a9411b01c8c11e6fe6 and we considered to patch provider classloader but decided to rolback that solution (https://github.com/gradle/gradle/commit/8ce9a84f56229240881d82bc736ba311ee5a1015).

I’m not sure that’s an answer to my question. It sounds like you are describing how things work internally.

What I wanted to know is, as someone who is using the tooling API, how can I write reliable code that uses BuildActions?

It seems I simply can not. I can only use BuildActions if the Gradle version that we connect to is >=2.3, but as far as I know, there’s no way to determine what the version is on the provider side. I.e. I have looked through the APIs for something where I can ask what Gradle version is being targetted and I couldn’t find it.

So my problem is that, I can not use BuildActions ever, since there’s seems to be no way to determine if they will work for the unknown version of Gradle we are connecting to.

Ah… I didn’t look hard enough. This is probably the answer to my question: http://gradle.org/docs/nightly/javadoc/org/gradle/tooling/model/build/BuildEnvironment.html

You can use the BuildEnvironment model to determine the target Gradle version.

Update on this: https://issuetracker.springsource.com/browse/STS-3973?focusedCommentId=104584&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-104584

Decided we won’t be trying to do a ‘good’ implementation of jar-remapping in the near future since its not practical to use BuildActions until Gradle 2.2 is ‘old’.