33

I'm making a task in gradle that needs to call a number of other tasks.

Here's what I have:

task ci(dependsOn: [
    clean,
    build,
    test
])

What is strange is the output I see:

gradle ci
:compileJava
:processResources UP-TO-DATE
:classes
:jar
:assemble
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test UP-TO-DATE
:check UP-TO-DATE
:clean             // cleaning after the build
:build
:ci

Note, that the clean occurs after the build target, which wipes out my build.

If I change my task to:

task ci(dependsOn: [
    clean,
    test
])

Then it appears to execute in the correct order:

:clean UP-TO-DATE    // cleaning before the build
:compileJava
:processResources UP-TO-DATE
:classes
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test UP-TO-DATE
:ci UP-TO-DATE

I tried fixing the original target by adding a build.dependsOn clean, but this seems to have no affect.

Any help is appreciated.

dev
  • 2,949
  • 5
  • 37
  • 48

4 Answers4

43

It seems I've stumbled upon the issue being debated in GRADLE-427 in which gradle determines the best order to execute the tasks. I resolved my problem by following the advice therein which is also documented in the Gradle User Guide - Section 15.5 to establish ordering between disparate tasks. My final ci target thus appears as:

task ci(dependsOn: ['clean', 'build', 'uploadArchives'])
build.mustRunAfter clean
uploadArchives.mustRunAfter build

And everything now works as expected.

dev
  • 2,949
  • 5
  • 37
  • 48
  • 1
    I've tried this approach in the past, but I don't like it because it introduces artificial dependencies. I've had problems in the past getting the order to be exactly what I need. I've found that the "alias" approach outlined elsewhere in this thread works better (and does not introduce artificial dependencies) – Lance Jul 27 '16 at 15:12
9

TLDR version: Here's how I did it in one of my projects (without introducing artificial dependencies).

//--- build aliases : define a synonym here if you want a shortcut to run multiple targets

def buildAliases = [
   'all' : ['clean', 'assemble', 'runProvisioner', 'stopTomcat', 'installTomcat', 'deployToTomcat', 'startTomcat'],
   'rebuild' : ['clean', 'assemble']
]
def expandedTaskList = []

gradle.startParameter.taskNames.each {
    expandedTaskList << (buildAliases[it] ? buildAliases[it] : it)
}

gradle.startParameter.taskNames = expandedTaskList.flatten()

println "\n\n\texpanded task list: ${gradle.startParameter.taskNames }\n\n"

In order to make use of these aliases, call them as tasks. Examples:

./gradlew all
./gradlew rebuild

or

gradle all
gradle rebuild

For further background, see :

https://caffeineinduced.wordpress.com/2015/01/25/run-a-list-of-gradle-tasks-in-specific-order/

MuffinTheMan
  • 1,509
  • 20
  • 25
Lance
  • 682
  • 6
  • 12
  • While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. – ZygD Oct 30 '15 at 00:29
  • Done. If someone takes the time to come here & explain things (and adjusts the answer based on your feedback), you shouldn't downvote it. This makes me not want to post here. – Lance Oct 31 '15 at 16:19
4

I would prefer not to add wrapper task just to make sure an order.

In this case, my solution is as follows -

run.dependsOn 'clean' compileJava.mustRunAfter 'clean'

This makes sure that, clean task executes before gradle executes compileJava. So effectively, you will create a fresh build all the time.

Hope this helps.

Kunal
  • 597
  • 7
  • 19
0

Using some Groovy/Gradle goodness the solution can be further improved with the following:

def taskNames = [...] // list of task names

task('lastTask', dependsOn: taskNames)

taskNames.inject(null) { acc, val ->
  if (acc != null) tasks[val].mustRunAfter acc
  tasks[val]
}

this way, you can have one place with a list of tasks.

Nikita Skvortsov
  • 4,768
  • 24
  • 37