[GRADLE-427] dependsOn doesn't respect dependency order Created: 25/Mar/09  Updated: 01/May/13  Resolved: 01/May/13

Status: Resolved
Project: Gradle
Affects Version/s: 0.5.2
Fix Version/s: 0.7, 1.6-rc-1

Type: Bug
Reporter: Marc Guillemot Assignee: Unassigned
Resolution: Fixed Votes: 59

Attachments: File execution.DefaultTaskGraphExecuter.patch    
Issue Links:
Duplicate
Duplicated by GRADLE-1356 Order of dependencies is not honored Resolved
Related
Related to GRADLE-1102 dependencies from ant depends="..." v... Resolved

 Description   

Using following:
//-----------------------------------------------
createTask("A1", dependsOn: ["B", "C"]) {
println "A1"
}

createTask("A2", dependsOn: ["C", "B"]) {
println "A2"
}

createTask("B")

{ println "B" }

createTask("C")

{ println "C" }

//-----------------------------------------------

gradle A1 and gradle A2 give the same result:
:B
B
:C
C
:A2
A2

which means that the order of the dependencies in the dependsOn list is not respected.



 Comments   
Comment by Marc Guillemot [ 25/Mar/09 ]

Oops, naturally for gradle A1, the end of the output contains A1 and not A2.

Comment by Hans Dockter [ 22/Apr/09 ]

We have to discuss this on the dev list. I will trigger this discussion when we start the work on 0.7.

Comment by Hans Dockter [ 06/May/09 ]

I have now run into this issue as well. I have posted something to the dev list.

Comment by Hans Dockter [ 11/May/09 ]

Hi Marc, would you tell us the real world problem where you needed those task relationships?

Comment by Marc Guillemot [ 13/May/09 ]

I don't remember exactly but it was nearly as simple as in the provided dummy example. I think that I had 2 tasks: compile and copy compiled stuff and I wanted to be able to execute both per default (in the right order) as well as to have the possibility to call each one individually.

Comment by Hans Dockter [ 11/Jun/09 ]

We have discussed this on the dev list. The full thread can be found here: http://markmail.org/thread/wn6ifkng6k7os4qn

Quote from Adam:

I feel pretty strongly that ordering of task dependencies is the wrong
way to go.

I have 2 main objections. Firstly, it really muddies the semantics of
the dependency graph. By ordering the edges, it stops being a DAG, and
becomes something else. It becomes unclear what it is and what it means.

Consider:

task a
task b
task c(dependsOn: b,a)

Why should task c have anything to say about the relationship between
tasks a and b, and how they should be ordered? If b really needs to
happen before a, then why is this not declared on b or a?

It becomes worse if you add a few indirections:

task a
task b
task c(dependsOn: b,a)
task d(dependsOn: a,c)
task e(dependsOn: c,d)

which of the above incompatible orderings do we choose? do we fail the
build? what if a,b and c are in a plugin outside our control? How does a
human understand what will happen here?

My second objection is that task ordering is, in practice, almost always
lossy. That is, there's something missing from the model to express the
actual reason why a particular task needs to be completed before another
one can start. Ordered dependencies are a poor approximation of this.

We should, instead, identify what it is that's missing from the model,
and add it in.

Comment by Szczepan Faber [ 12/Dec/09 ]

Hi guys.

I read the conversation on the mailing list (understanding most of it =). Allow me to put my comment here.

>Why should task c have anything to say about the relationship between
>tasks a and b, and how they should be ordered?

I think I get it though sometime it's just natural... example:

clean
build
rebuild(dependsOn: clean, build)

My real life example is:

task 'build-all' (dependsOn: [jar, 'sign-jar', 'build-tiddly', 'zip']) << {}

The order matters in my case. The reason I don't have explicit dependencies in some tasks is because it is way easier to work & debug tasks that don't have dependency. Every time I make changes to 'build-tiddly' task I can easily just run this task without triggering its dependency that is just time-consuming.

>My second objection is that task ordering is, in practice, almost always
>lossy.

Could be. However, not respecting the order feels counter intuitive. I spent some time and I couldn't understand why my build didn't work. I couldn't find it in the docs quickly so I ended up writing tiny gradle script to find out in what order dependencies are processed. dependsOn already offers predictable order (alphabetical, right?) but it's just not intuitive. I think processing dependencies alphabetically is not the right way of coaching build masters how to do proper DAG.

I don't want to argue what's the best solution - you guys decide on it. Just wanted you to know my feedback.

Keep on doing great stuff with Gradle!

Comment by Kurt Harriger [ 15/May/10 ]

I also found this behavior a bit unexpected.
I was trying to reproduce the maven lifecycle when I discovered this issue.
http://permalink.gmane.org/gmane.comp.programming.tools.gradle.user/4309

IMHO, the dependency graph should be processed using an in-order traversal with the additional restriction that each nodes are executed only once.
As Adam had noted on the discussion this could result in different execution order, however I do not find this confusing or problematic for my purposes.
What I do find confusing is why the dependencies are reordered, what is the advantage in doing this? or is it purely idealogical?

Comment by Hans Dockter [ 07/Jun/10 ]

@Kurt We see that there is a problem to solve that is not solved well yet by Gradle. We don't think that dependency order is a good solution, as pointed out. We want to address this issue in 1.0. But I see your point re the reordering and why to do it at all. Could you raise this issue on the dev list?

Comment by Hans Dockter [ 15/Aug/10 ]

I reopen this issue as it represents a problem still to be solved, although we want to solve it in a different ways then proposed.

Comment by Jakub Trojanek [ 07/Sep/10 ]

Just my two cents: I was enthusiastic about gradle until I hit this issue. This basically prevents transitional migration of our project, as I cannot import most of our ant targets due to their reliance on ordered "depends" list. So I would have been stuck with rewriting all ant targets.
IMHO atomicity and small targets/tasks you talk about in your discussion actually encourages using "depends" list, since you want to build bigger targets from small, reusable ones, executed in specific order

Another real life example:
releaseVersion = "test.release.version"
mailMessage = ""
task getVersion {
// sets releaseVersion property to user input
}
task releaseLogic {
//does release logic with releaseVersion
//customize mailMessage
}
task sendMail {
//sends customized mail to interested users
}

task release(dependsOn: ["getVersion", "releaseLogic", "sendMail"] {
//do regular release
}
I don't want any of subtasks to be dependend on other (I want to be able to do test release without forcing user input, and to send mail for tasks others then release) but I want release task to happen in exact order.

Comment by Hans Dockter [ 28/Sep/10 ]

Hi Jakub,

thanks a lot for your feedback. You are right, the ant migration aspect is another important thing to consider. We will solve this is in one way or the other.

Comment by Mark Smith [ 22/Sep/11 ]

Hi guys, I was just wondering if there was any more progress on this.

I found this issue after encountering a similar problem to Jakub while trying to set our Gradle project. Our use case is that we want to let our developers just run the war task for their grails app, but when we want to actually upload the war to our nexus server, we'd like to run clean before the war task. The most natural way to do this seemed to be having no dependsOn specified for the war task, but then have ["clean", "war"] for the dependsOn for the upload task, but from this discussion I see that that's not correct or reliable.

We're currently on 1.0 M3 and I was wondering if this is still going to be addressed for 1.0, as per the comment from Hans on June 7th.

Thanks,

Mark

Comment by Adam Murdoch [ 22/Sep/11 ]

@mark: We want to solve the 'clean must run first' use case. We're yet to decide exactly what is in or out for 1.0 release, but I'd like to include it if we can. However, we're very unlikely to solve the use case by changing Task.dependsOn to imply any ordering between the dependencies. Perhaps you could raise a separate jira issue for your particular use case, so we can tackle it separately.

Comment by Szczepan Faber [ 24/Sep/11 ]

>However, we're very unlikely to solve the use case by changing Task.dependsOn to imply

Too bad Albeit controversial, I find it the most natural thing to do It even feels consistent with our current approach to task names given via the start parameters - task names hint at order but ultimately it's the dependsOn on that is the master information for the DAG.

Anyways, I'm eager to see how we approach that issue

Comment by Blaine Simpson [ 06/Oct/11 ]

IMO the objections about implications to graph maintenance aren't justified. A preferred sequence would be just an additional constraint on the DAG. It's like saying that color-coding DAG nodes to indicate some aspect would harm its authenticity. As a matter of fact, the graph nodes already have a sequence, and it is the alphabetical ordering of the Task names, so we just want to apply a different ordering. In the real world we have practical needs, and the graph structures need to accommodate them. Even if DAG could not manage dependency execution sequence in a natural way, that would indicate that DAG is inadequate for a build system, not a need to preserve the structure by hacking weak and limited work-arounds (like command line switches to force a specific task to run first).

Just like I experienced with a different project a year ago, after championing conversion to Gradle and finally getting approval for the HyperSQL project I have to withdraw my recommendation due the embarrassing inability to control dependency sequencing in a manageable way like we have done easily and intuitively with Ant for 10 years.

Comment by Kurt Harriger [ 06/Oct/11 ]

I think I worked around the issue with something like this:
taskb.doFirst

{ taska.execute() }

I added bit of sugar adding to list then adding logic to each task automagically.
I was working elsewhere then and don't have the code anymore.
I have been meaning reinvent it and report back but not had time.

Comment by Blaine Simpson [ 08/Oct/11 ]

I just completed my work-around to preserve Ant build file dependency precedence specifications. Here you are:
http://hsqldb.svn.sourceforge.net/viewvc/hsqldb/base/trunk/build/build.gradle
This is the new Gradle wrapper for HyperSQL's main Ant build file, and most of it is my dependency-sequence work-around.

There is some unrelated stuff in there. The methods and functions of interest here are

  • recursivePush(...)
  • resequenceAntDependencies()
  • The top-level code under the comment "// RESEQUENCE"
  • tasks

My 'tasks' override makes up in large part for the lack of a private task mechanism to hide tasks similarly to Ant non-described and -* tasks.

My system supports nested Ant imports, but you must specify all file names (as you can see I have done). Would be easy to pull in the imported file names automatically, but I though it best to leave that decision up to the invoker.
Supports custom Ant build file adjustments by an optional closure parameter to resequenceAntDependencies().
Your Ant targets may not be named with 3 leading digits, because I use that name space for my renaming targets to accomplish the desired ordering.
As you can see by running "gradlew tasks" (or similar), I add wrappers for the renamed tasks, copying over the descriptions, then hiding the renamed ones, so that "gradle tasks" looks just like "ant -p" before you introduced Gradle.

Checks are made for dependency cycles, where in one dependency spec we have A... B and in another B... A. Doesn't handle this, but my highly-nested 74-target Ant build file doesn't have any, so you probably won't either.

Unrelated to the dependency sequencing, but of interest to others wrapping Ant, I use the custom closure and the resequence.destDir option to work around Gradle's multiple terrible bugs that prevent the basedir specified in the Ant build file from working.

Comment by Szczepan Faber [ 14/Oct/11 ]

@Kurt, execute() method only executes 'actions' attached to the task via doFirst() / doLast(). So your workaround may not work for all kinds tasks. For example, most built-in tasks/custom tasks don't have any doFirst/doLast actions by default.

Comment by Daniel Plappert [ 10/Nov/11 ]

It is very important to set the order in which tasks should be executed. I came across this bug while I was creating a task for testing our web-app. Before webtests can be executed, the schema has to be created, the jetty must be configured and so on:

task createSchema << {}
task dropSchema << {}
task startJetty << {}
task runWebtests (dependsOn: [dropSchema, createSchema, startJetty] << {}

In this case, the order of execution is very important, i.e. the task dropSchema is executed before createSchema, which is currently impossible. And I prefer not to change the task createSchema by adding a dependOn dropShema. This would be just a workaround. Also in some scenarios I don't want to drop the schema before creating one, because in this way some other tasks will cause problems.

There are many scenarios in which the order is important and has to be set by dependsOn. Currently we have to create a workaround to solve this issue, but it would be very nice, if this "bug" is fixed in one of the next versions.

Comment by Scott Stewart [ 14/Nov/11 ]

Wanted to add my sentiment, like others, this may prevent us from moving to Gradle. We have a large, complex, existing ant build that is highly dependent on the tasks being executed in order of dependency declaration. Requirement #1 for any build changes is the ability to do it incrementally. I don't even know how anyone can say in good-faith that Gradle supports existing Ant builds when it doesn't respect the order of dependency declaration. I've seen dozens of Ant scripts and the reliance of this ordering is everywhere.

Certainly the dependent tasks need to be executed in some order. Is the arbitrary choice of alphabetical order provide any benefit whatsoever?

Comment by Krystian Szczęsny [ 01/Dec/11 ]

Was there any progress on this issue?

I read Adam comment on the mailing list and I can't imagine how he came up with it. I guess view really depends on what you want to use gradle for. For his question:

It becomes worse if you add a few indirections:

task a
task b
task c(dependsOn: b,a)
task d(dependsOn: a,c)
task e(dependsOn: c,d)

which of the above incompatible orderings do we choose? do we fail the
build? what if a,b and c are in a plugin outside our control? How does a
human understand what will happen here?

I'm not sure what Adam means by this list, but I assume it's a list of tasks ran by the user. If so [for me] it's quite simple, you run task a, then you run task b, then you run task c [since b and a were already ran], then you run task d [since a and c were already ran] and finally you run task e [since c and d were already done].

If it's just a definition of tasks and user decides to run d and e, then you run a, c, d, e.

I think you want to add some intelligence to gradle, which is not required. Let users decide what to run and in what order. If something was ran then don't do it again [or allow user to specify dependencies which have to be re-run even if they were already].
If there's an error, let the USER decide what to do and ask them to rewrite the dependencies. If they have a tasks:

task a
task b
task c
task d(dependsOn: a, b)
task e(dependsOn: d, c)

and they want to run task e without running a, what would you tell them? Just rewrite the tasks.

Currently, I've got tasks like:

checkout from VS
clean
build
package(dependsOn: clean, build)
stop server
do some cleanup on server
copy package to server
start server

So I have all this little tasks, with their dependencies and in the end I wanted to have a deploy task:

deploy(dependsOn: git, war, stop, cleanup, copy, start)

I was pretty shocked, when I saw gradle reordering all the tasks and trying to execute:

cleanup
copy
git
start
stop
war

I understand that what is going on behind the scenes is complex and there are things about gradle I have no clue about, but in the end it should be me who decides the order of things.

Comment by Andres Almiray [ 12/Dec/11 ]

If I could give this issue more votes I would.
I'd love for Gradle to honor my settings and execute tasks in the order I specified them. I don't want to rename task names nor reorder task dependencies just to please the tool (that's the maven way, remember?)

Comment by Christian Mouttet [ 11/Jan/12 ]

I did some debugging and found a wrong usage of TreeSet<Task>. org.gradle.api.DefaultTask.Task is a Comparator that sorts alphabetically by its path.

Just changing it to a List and reverse its original order helps a lot.

The attached patch is based on branch REL_1.0-milestone-7.

Comment by Paul Jimenez [ 17/Feb/12 ]

IMO, this is NOT a bug, though it does point up a deficiency in Gradle.

Dependency order should not be respected; doing so breaks Gradle the same way ant is broken, and keeps it from being able to (in the future) do parallel builds.

Instead, Gradle needs a facility to easily invoke one task from another, procedurally. Saying that 'a depends on b and c' says nothing about the order of b and c. Saying 'a needs to do first b, then c' does, and Gradle needs to be able to express both.
The first should be:

task a(dependsOn: [b, c])

but the second could be either:

task a {
    b.execute()
    c.execute()
}

or:

task a(dependsOn: b) {
    c.execute()
}

...though I'm enough of a noob to not know if there's actually an 'execute()' method on each task (or what it's actually called if it does exist).

Comment by Tom Widmer [ 28/Feb/12 ]

I think at heart this is about the ability to specify that:
A should always happen before B, even though b doesn't depend on a.

You could have something like Task.orderAfter to specify this. This method wouldn't imply a dependency, just that if both tasks are scheduled, one should happen after the other. I guess you end up with two DAGs - one for dependencies, and one for explicit ordering (which will usually be small). The commandline list of tasks could also add edges to the ordering DAG (e.g. a commandline of "clean build" will implicitly call "build.orderAfter clean"). Merging the two might be as easy as this: for each edge in the ordering DAG, if the two ends are in the dependency DAG, add the edge in from the ordering one into the dependency one (it's probably harder though!).

In my case, I just want to get a continuous integration build to do things in the right order. My workaround is:

def whenScheduled(task, closure) {
    if (gradle.startParameter.taskNames.contains(task)) {
        closure(task)
    }
}

task cibuild {
    project.buildType = 'build'
    dependsOn 'clean', 'test', 'artifactoryPublish'
    whenScheduled(name) {
        gradle.projectsEvaluated {
            project.artifactoryPublish.dependsOn 'test'
            project.compileJava.dependsOn 'clean'
        }
    }
}

which is pretty much applying a limited version of the DAG merging manually!

Comment by Phil DeJarnett [ 28/Feb/12 ]

@Tom Widmer

I think the way you are hacking a solution leads to a possible fix that doesn't require a second DAG: Add a command that says, effectively, dependsOnIfActive(task)

This would allow the current DAG to order the events, but if the task is not requested explicitly or implicitly, it's not included in the tree. I don't know if this would work for every case, but it might be a good enough compromise. It also doesn't require any explicit task ordering.

Comment by Connor Garvey [ 21/Jun/12 ]

Paul's solution is the simplest. The semantics of dependsOn aren't correct for the situations most people are describing. A CI build doesn't depend on other tasks to be done. It is those tasks. Allowing tasks to directly execute other tasks is straightforward and logical and doesn't force developers to learn a new language. In my case, I have a build in which I want to ...

try { startServer; integrationTest }
finally { stopServer }

I want to just do that.

Comment by wujek srujek [ 14/Jul/12 ]

Hi. My 2cent maybe: I have the use case:
1. I have tests (fast) and integration tests (slow)
2. I want to be able to run test without integration tests, and integration tests without tests
3. check should run test and only when test succeeds should it call integrationTest (test has fast unit tests, if they fail, I am not really interested in integrationTest, at least on my local machine; a CI environment might have another requirements, but that's another issue); I expected this:
check

{ dependsOn integrationTest // as it already depends on test, integrationTest appends the dependency // or at least dependsOn = [test, integrationTest] // just set the list }

to do what I want. To my surprise, this is completely ignored!

I think this is utterly inconsistent with the fact that when I specify the tasks on the command line, their ordering is taken into account, unless it is inconsistent with the task DAG. So when I do:
gradle test integrationTest
I get the ordering I need (kind of). But that's so much to type ;d and it also doesn't really work, as gradle (at least 1.0 on my machine) does 2 'passes' over the projects: suppose project A and B, where B depends on A. When I invoke gradle test integrationTest in the root project, I get (I don't list all the tasks, just showing the concept):
A:compile
A:test
B:compile
B:test
A:integrationTest
B:integrationTest
which is also not what I want (I want to build a project fully and only then build the next one). Ant can do it...

Comment by Trevor Samaroo [ 02/Aug/12 ]

Instead of defaulting to the user defined order in dependsOn, you default to ordering based on the alphabet. You think this is better? you could just as easily default to a DAG influenced by the dependsOn definition (user defined ordering) instead of influencing the DAG with an alphabetic sort routine - no? Maybe make it a property of gradle ... gradle.taskDependencyMode=Alphabet vs User Defined. Its scary to think of what happens to builds when you simply rename a task and the poor developer running the build isn't a gradle expert.

As an example - the third dependency here only runs third (before doTag) because i *changed its task name* from "doVersion" to "doSVersion".

task checkOutVersionAndTag(dependsOn: ['checkout','doSnapshotCheck', 'doSVersion', 'doTag']) << {}  

sometimes you want to run a task before another one, and at other times you don't. you want to have some say in ordering tasks.

i don't want doTag to depend on doVersion because i don't need to version all the time i call doTag. and its a huge hack to name tasks in such a way that they execute when you want them.

i don't see any clean way to do something so simple. We're just users of this build system and i think we just want this as a feature. I wish Gradle would be more practical about this issue - its supposed to solve frustrations with other build systems, but has seemingly illogical limitations (reminds me of not being able pass in a maven pom version into a maven build.)

Comment by Mauro Molinari [ 03/Aug/12 ]

I'm voting for this bug, not because I want dependsOn to necessarily respect the order of the specified tasks (once it is well documented), but because I need a way to solve my problem.

This is my use case. I have a task that generated Java files from XSDs. The translation XSD->Java is not done at build time, but only on-demand when needed (i.e.: when the XSDs change and at development time a new Java src generation is needed). So, my compile task, that compiles those generated Java files for building, must not depend on the Java source generation.
However, I have then another task that is called on-demand when needed and that does some post-processing to the generated Java files (producing other artifacts). This post processing needs as inputs both the generated Java files and the corresponding byte-code obtained by compiling those Java files.
So, when I run the post-processing task I need to

  • run the Java src code generation (if not up-to-date)
  • then compile that generated code
  • and last run the post processing code
    That is, the post-processing task depends on both the src-generation task and the generated-compile task, but the dependency must be in this exact order: of course, I can't compile the source if it has not been previously generated.

I then discovered that dependsOn(A, B) does not guarantee that A is executed before B. No problem for me, but I can't find a proper way to express this.
Either I must be able to call A and B from the post-processing task without using the dependency relationships or I must find a way to "clone/extend" the compilation task (hopefully without having to redefine it from scratch) by adding the dependency towards the generation task, and then make the post-processing task depend on this new compile task instead of the default one (that must then be used only at build time).

Just my contribution. Any suggestion is welcome.

Comment by Carus Kyle [ 21/Aug/12 ]

My two cents..
dependsOn is alright how it is, instead building off Paul's suggestion I am thinking of something like a Meta-Workflow type task.

task install_all_the_things(type:Workflow) {
  // task list here, maybe a few options?
  // could even facilitate progress bar functionality?
}

each task can still have its dependsOn: field for what it really depends on, not just to force an order. This would allow you to call individual tasks when needed without triggering a whole huge chain and still call the whole chain for defined workflow.
Deploying into our middle tier servers takes a bit of dancing around. this issue I have dealt with by just running #gradle taskA taskB taskF taskG taskL from the command line. Some of the tasks are useful utilities that on occasion need to be run by themselves and as part of an overall deployment.

Comment by Adam Murdoch [ 22/Aug/12 ]

@Carus Kyle, this is more or less what we're looking at doing.

Comment by Daniel Gredler [ 24/Aug/12 ]

I'm a little bit hesitant to play devil's advocate when the consensus seems to be that dependency ordering is a bad idea, but here goes!

Regarding Adam's objection that respecting task dependency order degenerates the task DAG into some sort of "DAG+", I would say this is true. However, is it better to have a single "DAG+" task model, or is it better to split it into a pure "DAG" model augmented with a separate "+" model which contains the ordering information?

I understand that a pure DAG is very attractive, but we're agreed that it can't fully express the user's intent in all scenarios. At this point the question seems to be whether the extra information is added to the primary task graph model or delegated to a secondary "workflow task" model that enriches the primary model. I would be especially concerned if we end up with a declarative "DAG" model and a procedural "+" model.

It is worth mentioning that many commonly-used tools in the Java world have addressed variants of this problem: Ant [1], which respects order (I'd be very interested to learn more about how "ant is broken" because of this, as Paul says above); TestNG, which does not respect order on test dependencies [2], but does provide some support for test ordering [3]; Spring, which I believe does implicitly respect bean dependency order [4].

Regarding Adam's objection that a "DAG+" is lossy and doesn't fully express the rationale for the ordering, I'm having trouble conceiving of a solution that meets that goal in a better way. If someone (e.g. Daniel Plappert above) wants to create a "runWebTests" task that depends on "dropSchema", "createSchema", and "startJetty" (in that order), then I can't think of a solution that more clearly or succinctly expresses that in this context "createSchema" comes before "startJetty", but "startJetty" is also intended to be run independently sometimes.

We should also consider the principle of least surprise. Gradle is a Groovy DSL, and in keeping with idiomatic Groovy, users will intuitively expect that [a, b, c] will be an ordered list and used as such. It would be convenient if Groovy had a better notation for unordered sets, and then the behavior of the dependency collection could vary according to the collection type (ordered or not). Unfortunately, the easiest collection shorthand is for a list, meaning that most people would use lists by default, and then almost nobody would be able to benefit from e.g. build parallelization further down the road.

Maybe a new workflow task is the best solution... but would it be limited to use as an "entry point" task, or would other tasks be able to depend on workflow tasks? If limited to use as an "entry point" task, it would probably limit the impact of having this separate "+" model, but I'm not sure it would cover all use cases. If not limited to use as an "entry point" task, why not just keep it all in a single unified "DAG+" model? Either way, you seem to end up extending the model in a way that muddles the task DAG.

Finally, an off-the-wall idea: I know you can do operator overloading in Groovy, although I've never had to do it myself. You could probably make the following DSL express ordering, while keeping the current default unordered behavior:

task runWebTests (dependsOn: dropSchema >> createSchema >> startJetty) // all ordered
task runWebTests (dependsOn: [dropSchema >> createSchema >> startJetty]) // all ordered
task runWebTests (dependsOn: dropSchema >>> createSchema >>> startJetty) // all ordered (use unsigned right shift instead of right shift?)
task runWebTests (dependsOn: dropSchema >> createSchema >> [startJetty, startFtpServer]) // all ordered, except Jetty and FTP can be started in parallel
task runWebTests (dependsOn: [dropSchema, createSchema, startJetty]) // no guaranteed order, like now

Sorry for the wall of text... thanks for reading!

[1] http://ant.apache.org/manual/targets.html
[2] http://testng.org/javadocs/org/testng/annotations/Test.html#dependsOnMethods()
[3] http://testng.org/doc/documentation-main.html#testng-xml
[4] http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/context/annotation/DependsOn.html

Comment by wujek srujek [ 24/Aug/12 ]

Not having thought about it much, I must say the above examples (dropSchema >> createSchema >> [startJetty, startFtpServer]) look very appealing and are really expressive in my opinion.
+1!

Comment by Ryan Shillington [ 28/Aug/12 ]

Yes, +1. I agree with Daniel Gredler's answer above as well, although modifying the default behavior to execute dependencies in order is what I would do personally. Put it in the change notes for v2 and be done with it.

As a long time lurker, I signed up for an account just to vote for this. I saw this issue when I was using milestone-6 and left it because, well, surely it'll get solved. It's such an obvious feature request and such super important functionality. I'm surprised that the Gradle community has allowed this to fester for so long.

A good Gradle best practice is to never write a task that depends on the alphabetical ordering. You know that's going to burn you when somebody modifies the name of a task. It's just bad coding practice. We use .execute() instead, which I know, is also bad. It won't burn us without upgrading Gradle though.

Comment by Adam Murdoch [ 03/Sep/12 ]

@Daniel, good points.

There are a few things I want to comment on.

The workflow step (or build type as we're calling it) is intended to be a starting point, not the final destination. It's easy to implement, solves maybe 70% of the use cases we've seen, and allows us to drive work on a richer task execution model that can later become public.

The build types are not just about task ordering - they're declarative elements that describe the public entry points of the build, the inputs that they take (eg when I'm doing a release build, I need to provide credentials for the repository), and the conditional logic that has to happen for that build (eg when I'm doing a CI build, wire in the test coverage report).

There's a lot of value in build types even if they don't solve everyone's task ordering problems. But they are a step towards a more general solution.

The plan is certainly not to end up with separate declarative DAG and imperative + pieces. Instead, we will have a high level declarative model that describes what you want to build, and a low level declarative model of how that should happen. The high level model is used to assemble the low level model. They both describe the same thing, just with different views: The high level model is a graph of things that depend on each other, and the low level model is a graph of actions (tasks) to be executed to achieve the result.

We already have this layering: when you declare a project dependency, you're not saying anything about which tasks to run. You're just stating: 'my library foo depends on library bar, please make sure it's available before attempting to compile foo'. From this high level declaration, the low level task dependencies are wired in.

And so with the canonical 'start jetty then run my tests' use case. There is a hard dependency here: my tests need my web app to be running. This this isn't a task dependency. The tests don't care how the web app was deployed. It's a dependency between things: my web app and my tests. So, in the high-level model, you'd state 'my tests need my web app to be running', and Gradle would turn that into the appropriate low level task dependencies. The jetty plugin, for example, might declare 'I know how to deploy a web app' and Gradle can ask it to do so.

Adding in the database schema, we see the same thing. There is a hard dependency here: my web app needs a database instance to be running with my schema. Again, it's not a task dependency, it's a dependency between things: my web app and the database instance. In the high level model you'd state: my web app needs a database instance with my schema, and Gradle would turn that into the appropriate low level task dependencies. Maybe the mysql plugins declares 'I know how to create a database instance' and the liquibase plugin declares 'I know how to apply a schema to a database instance', and Gradle can ask them to do so.

Why is this useful?

Firstly, by declaring the dependencies at the right level of abstraction, you keep things simple. There's no need for dependency ordering or any of that. It's also declaring the actual dependencies, not the dependencies denormalised into a set of tasks.

Secondly, you keep things flexible. Gradle can choose the appropriate tasks based on the current state of the world: If there's already a database instance with the schema applied, then just delete all the rows and reapply the test data. If there's database instance with the wrong schema, then drop all the tables and apply the schema. If there's no database instance, create it and apply the schema and test data. If the tests are going to be run in parallel, then spin up a separate database instance for each test worker. If I'm doing a developer build, reuse the instance I have on my local machine. If I'm doing a CI build, then rebuild the database instance from scratch in the QA environment.

So, we have a nice high level description of the relationships between the software components. Ideally, you work only at this level and never touch the task model.

However, there will still be cases where you need to work with the task model, for whatever reason. This will still be a graph, but with several different types of edges, or relationships. There will be no orderings between the edges. Currently, we're looking at the following relationships:

  • Tasks from set A must run before tasks from set B. This is our existing depends-on relationship, but generalised a touch to handle things like: clean and its dependencies must run before assemble and its dependencies, or all validation tasks should run before any other task.
  • Tasks from set A should run before tasks from set B. This covers things like: honoring imported Ant task ordering, or unit tests should run before integration tests (but don't have to), or all jar tasks should run before any upload tasks.
  • Tasks from set A must run if any of the tasks from set B run. This covers things like: always generate a coverage report if the coverage check task is run even if the coverage check fails, or always generate an aggregated test report if any test tasks are executed, or always stop the Jetty server at the end of the build if the Jetty server is started.
  • When any task from set A is scheduled to be executed then (any of the above). This covers things like, if the coverageReport task is to be executed, then instrumentTestClasses must run before test.

There will be some conveniences for the above. Maybe even your proposed syntax, so that:

task a(dependsOn: b >> c)

is shorthand for:

tasks (b,c) must run before task a, and when task a is scheduled then task b and all it dependencies must run before task c and all its dependencies.

Comment by Attila Kelemen [ 04/Sep/12 ]

I'm not sure if I should open a separate issue or not but my RFE could probably offer a solution to this problem.

I also sometimes needed to execute tasks in an order but cannot declare a depends on relationship on them (like with the rebuild task). What I was really looking for when doing this is an aggregator task. That is, to me specifying an order between edges of the graph seems completely counter intuitive. But adding a method to Project to create an aggregated task from multiple tasks seems to be the way to go.

For example:

aggregatedTask(rebuild).addTasks(clean, build);

Here rebuild is an instance of org.gradle.api.Task.

Comment by Bretislav Wajtr [ 02/Oct/12 ]

+1 for resolving the bug, because of this problem, gradle is a no-go for us and we have to stick with Ant for the time being

Ugly workaround I came with (showing that user-defined task order really has meaning).
Wanted to create simple "rebuild" task (clean+build) and I had to do it like this:

task xbuild(dependsOn: build)
task rebuild(dependsOn: ["clean", "xbuild"])

(== gradle internally sorts dependencies alphabetically, so "renaming" build task to xbuild solves the problem and the clean task is performed before build task this way)

I have desired functionality, but...

Comment by Taylor Brown [ 10/Jan/13 ]

I too was surprised to see that this didn't work. I previously had a set of tasks defined on the command line, and of course task ordering was respected there. When I decided to move this set of ordered tasks into a single task, I had not way of doing it. My "Build" task does not "depend on" my "clean" task. But if I'm cleaning AND building, I sure as heck want my build to come after clean. I look forward to this being solved. Thanks!

Comment by M. Chelfi [ 07/Feb/13 ]

I have a naive suggestion, why not use a List<Task> in the dependsOn instead of a Set<Task>? this would ensure iteration order an honor the task order.
I am using gradle 1.4 and the issue (or feature) is still there, I wanted basically like one of the use cases mentioned before depend on clean before building and archiving since 'c' in clean is after 'b' in build, although put them in clean, build order, the build was run first as the internally data structure used is Set for the dependsOn

Comment by Paul Jimenez [ 07/Feb/13 ]

Switching behavior on whether the parameter was a List or a Set might be an okay compromise. It's a bit subtle, but as long as it's documented it would allow both desired behaviors.

Note that it uses the 'expected' (ordered) behavior more easily since it's a little easier to construct a List than a Set in Groovy. Consider:

task a(dependsOn: [b, c])

would mean execute b, then c, then a, but

task a(dependsOn: ([b, c] as Set))

would mean execute b and c in any order, and then a.

Sounds like a good solution to me! Who's up for implementation? Something like:

if (dependsOn instanceOf Set) {
  // existing behavior
} else if (dependsOn instanceOf List) {
  // iterate over the list of tasks
}
Comment by Matias Bjarland [ 07/Feb/13 ]

For the two last posts: I'm fairly certain this issue is not a question of how to implement an ordered dependency notation. The question is philosophical, not technical: should the dependency graph be ordered or not?

There are (as per previous comments above) benefits to an unordered set and reasons for keeping the default and large majority or dependecy declarations unordered. Last I heard gradleware was leaning towards an extra notation (i.e. not replacing the old one but creating an additional one) for the potential ordered dependency list.

Comment by M. Chelfi [ 07/Feb/13 ]

Matias, I did read the previous posts and I agree with you it's a philosophical view on the question. I do not exactly know how the internals of gradle would handle the implication of looking at the dependsOn as ordered but having the impression that at some point it decides to iterate through the dependent tasks I thought replacing the underlying Set structure with List would do the trick without compromising the DAG, that is why also I started my post by saying a naive suggestion.

Comment by Paul Jimenez [ 07/Feb/13 ]

@Matias: actually, the question is how to implement an ordered dependency notation. It's fairly well agreed that the current bug title "dependsOn doesn't respect dependency order" isn't the real bug. The real bug is that there's currently not a good way to notate ordered dependencies. It's not the philosophical question of "should the graph be ordered or not" because the dependency graph is ordered, there's just not an easy way to express this additionally desired ordering (intermediate 'throwaway' tasks would do it, but that's not easy.)

I put up one strawman solution last February, M. Chelfi's suggestion for said syntax is as valid as the other proposals.

Comment by Adam Murdoch [ 10/Feb/13 ]

For those who are interested, there has been some recent discussion of this on the dev mailing list, in this thread:

http://gradle.1045684.n5.nabble.com/Gradle-reporting-improvements-tt5710408.html

The relevant stuff is a little way into the thread, starting at:

http://gradle.1045684.n5.nabble.com/Gradle-reporting-improvements-tp5710408p5710802.html

Everyone is welcome to join in the discussion there.

Comment by Nathan Wells [ 25/Mar/13 ]

See GRADLE-2721 for a possible improvement that would solve this.

Comment by Adam Murdoch [ 01/May/13 ]

Gradle 1.6 adds support for task ordering rules, which solve many of the use cases listed in this issue.

We're not finished with task ordering and we haven't yet solved all the use cases here, but I've marked this issue as 'fixed'. If you find problems or want to suggest improvements for task ordering and execution, please feel free to raise new issues via the forums.

Please note that we're very unlikely to change dependsOn to run tasks in the order listed, as requested in the title of this issue.

Generated at Wed Jun 30 11:29:50 CDT 2021 using Jira 8.4.2#804003-sha1:d21414fc212e3af190e92c2d2ac41299b89402cf.