Is it possible to directly call a task or to influence the optimized task graph?

I have a complicated build which requires one task graph which downloads an artifact, unzips it, explodes a war inside of the zip distro, then executes an arbitrary JavaExec task to create one artifact:

projecta:

task download
task unzip(dependsOn: download)
task explode(dependsOn: unzip)
task noconsole(dependsOn: explode, type: JavaExec)

I have another portion of the build which needs to do all of those things, but before running the JavaExec task, it needs to insert another step.

projectb:

task stage(dependsOn: explode)
task noconsole(dependsOn: [stage, ':projecta:noconsole'])

Unfortunately, the task graph optimizes, and the order of execution is wrong… :projecta:download :projecta:unzip :projecta:explode :projecta:noconsole <— undesirable :projectb:stage

:projectb:noconsole

How can I set up a build which allows me have the noconsole tasks in both projecta and projectb, but the one in projectb relies on inserting projectb:noconsole BEFORE projecta:noconsole executes?

I can’t quite follow your question. Which exact order do you want to see, and which task are you executing in this case? Right now you have declared that :projectb:noconsole depends on :projecta:noconsole, so the former cannot possibly be executed before the latter.

Hi Peter, sorry for the lack of clarity in the original question. There is a typo, the last line should have read “… inserting :projectb:stage BEFORE :projecta:noconsole …”

Due to the order of elements in the dependsOn list, the order of execution I expected was:

:projecta:download

:projecta:unzip

:projecta:explode

:projectb:stage

:projecta:noconsole

:projectb:noconsole

The code that is generating the task Graph pulls all of the projecta elements up before projectb, even though :projectb:stage does not rely on :projecta:noconsole.

My original thought was to just manually call :projecta:noconsole as the last action of :projectb:noconsole, but I was unable to find anything in the API or DSL that suggested such a thing was possible (or wise). That’s why I tried to get inside of the dependsOn hierarchy.

I’m also open to hearing an alternate approach to the problem. I’ve been staring at this too long and may have lost perspective on the right way to solve the problem.

In Gradle, this:

task noconsole(dependsOn: [stage, ':projecta:noconsole'])

is equivalent to:

task noconsole
noconsole.dependsOn stage
noconsole.dependsOn ':projecta:noconsole'

This explains what you are seeing.

To get the desired behavior, you could weave in :projectb:stage conditionally in the case :projectb:noconsole is to be executed. The earliest you can tell reliably is gradle.taskGraph.whenReady {}, but unfortunately you can’t add further task dependencies at this point.

In some cases it’s good enough to check gradle.startParameter.taskNames to find out whether a task is going to be executed, but for that you’d have to at least give “:projectb:noconsole” a unique task name (say “:projectb:noconsoleb”). Then you could do:

if (gradle.startParameter.taskNames.contains("noconsoleb")) {
  project(":projecta").noconsole.dependsOn(stage)
  stage.dependsOn(project(":projecta").explode)
}

However this will only work for the case where :projectb:noconsoleb is executed directly. I expect Gradle to offer a more complete solution in the future.

The other options is to rethink the bigger picture. Why do you need this behavior? Is there a different way to model it?

Thanks, Peter. I was not aware of stage.dependsOn(). I’ll try it out later and see if that can accomplish what I need. In terms of modeling it a different way, there very well could be a better way, but I haven’t thought of it yet.

The earliest you can tell reliably is gradle.taskGraph.whenReady {}, but unfortunately you can’t add further task dependencies at this point.

Shouldn’t that restriction make it into the user guide and the DSL?

Instead I assume the correct way to add dependencies between tasks is by using gradle.projectsEvaluated(closure) … ?

Typically there is no need to wrap a ‘dependsOn’ with ‘gradle.projectsEvaluated {}’.