How can define task dependencies in a multi-project configuration using injection?

I have a multi-project setup all the subprojects build out of the same directory hierarchy. eg they are all unzipped on top of each other in the root directory, and have to coordinate amongst themselves to avoid file collisions. Unfortunately changing this structure is not an option.

The subprojects all have zero or more compile dependencies on each other, which are defined externally. We have other tools that also require this information, so we cannot migrate the dependency definitions into the projects themselves.

The current strategy I’m working with is to have each subproject define its own build.gradle file for compiling itself, and then to dynamically build the root’s build.gradle file based on which projects are present. I’d like to inject dependency information then into the root project, and have gradle take care of building the subprojects in order.

root build.gradle template

subprojects {
    task build { }
    task dist(type: Zip) { task ->
        from System.getProperty("user.dir")
        doFirst {
            new File("zips").mkdir()
            archiveName = "zips/" + task.project.version + "_bin.zip"
        }
}

subproject build.gradle template

version = "projA_v1.2.3"
  build {
    doLast {
        // do actual build logic
    }
}
  dist {
    includes = ["bin/projA_*"]
}

How do I define that the build task from projA depends on the build task of projB? I would like to be able to just call ‘gradle build dist’ and have everything build according to the dependencies, and then dist (in any order, doesnt matter). However, if I need to call the build and dist tasks separately as 2 commands, that is okay.

I’ve tried to follow the examples from chapter 56 and 59 of the User’s Guide, but I’m not entirely sure what’s happening, and the tasks end up executing in alphabetical order anyways:

project(":subdir/projA") {
    dependencies {
        build project(":subdir/projB")
    }
}

I’m also open to other suggestions as to how to go about getting everything together. Thanks in advance

gradle version for good measure:

------------------------------------------------------------
Gradle 1.12
------------------------------------------------------------
  Build time:
 2014-04-29 09:24:31 UTC
Build number: none
Revision:
   a831fa866d46cbee94e61a09af15f9dd95987421
  Groovy:
     1.8.6
Ant:
        Apache Ant(TM) version 1.9.3 compiled on December 23 2013
Ivy:
        2.2.0
JVM:
        1.7.0_51 (Oracle Corporation 24.45-b08)
OS:
         Linux 3.11.0-19-generic amd64

After some fiddling around it appears that this is working:

project(":subdir/projA") {
    build.dependsOn project("subdir/projB").tasks.build
}

can anyone confirm this is the proper way to go about injecting task dependencies?

This likely won’t have the desired effect. It only defines dependencies between the ‘build’ task in ‘projA’ and tasks in ‘projB’, but not between any other task in ‘projA’ and tasks in ‘projB’. Instead you should try to model the real task dependencies, whatever they are (e.g. ‘compileJava.dependsOn(":projB:jar")’). However, typically it’s best to avoid explicit dependencies between tasks in different projects in favor of depending on another project’s configuration(s). Something like:

dependencies {
     compile project(":projB")
     compile project(path: ":projB", configuration: "other")
}

Can you elaborate a bit more on the difference between task dependencies and configuration dependencies, please? What kind of problems would you expect to see when using task dependency injection?

The subprojects being built are C or C#, and currently provide their own make files. We expect the majority of project teams will simply invoke these from their new build.gradle files, rather than port the logic to gradle. Either way, we only really care about dependencies for the build task - the make files need to be called in a specific order, but not so for the dist tasks. So long as the build task succeeded, the dist task can be called whenever, and it’ll just package up the output of the build task.

Thanks

Manually managing cross-project dependencies can be tedious and prone to error. It may also not work well with future Gradle features. As long as you just want to wire together a bunch of ‘build’ tasks, you are probably fine. (But then you aren’t really using the power of Gradle.)