Dependencies and multiple projects (non-java)

Hi,

I would like to leverage tasks and dependency resolution of Gradle for a non-java project. I have read the documentation, a few threads in SO and in this forum, but I still don’t understand how it works.

Here is a simplified version of my use case:

project structure

myproject/
  a/
  b/
  build.gradle
  settings.gradle

settings.gradle

include ':a', ':b'

build.gradle

allprojects {
  configurations {
    foo
  }

  apply plugin: "base"

  task test {
     doLast {
       println "Yeah"
     }
   }
}

project(':a') {
  dependencies {
    foo project(':b')
   }
}

If I run gradle test the task is executed first on a and then b. I would have expected the opposite:

# gradle test

> Task :test
Yeah

> Task :a:test
Yeah

> Task :b:test
Yeah

BUILD SUCCESSFUL in 0s
3 actionable tasks: 3 executed

In your example, there are zero dependencies that would influence order of execution for your test tasks.

This code configures a dependency from the foo configuration of project a to the main output of project b. However, unless you have a task that consumes foo as an input, it won’t influence task ordering.

Even in a Java project, these test tasks wouldn’t have a guaranteed order unless you set dependsOn or mustRunAfter, or shouldRunAfter between them. They would depend on the compilation of the main project code and the test code, so more dependencies of :b:test would be met before :a:test.

As your example project is simplified, it’s hard to know what is most sensible for your actual project. It may be explicitly configuring mustRunAfter / shouldRunAfter for those test tasks. The main thing is that a project dependency will not control the order of isolated tasks in that project.

1 Like

Thanks, that’s exactly the part I misunderstood!

So I should write something around these lines, correct?

 task test {
     inputs.files(configurations.foo) 
     doLast {
       println "Yeah"
       // use configurations.foo
     }
   }

On the consuming side, yes, but nothing in your example really screams producer.

Typically you’d have something in project b that would produce a file and you’d configure it as an artifact. That could be an artifact of the foo configuration on project b. Depending on what it is, you may need to use builtBy. Once you have that, the domain modeling would cause the relevant task in project b to run before the relevant task in project a. Generally that doesn’t make sense for the test task of multiple projects though.

If it’s just a test task that must run before another test task, you’re better off just configuring that. It doesn’t make sense to force artifacts and dependencies into the model if that’s not really what you have.

So let’s say the test task produces an archive and needs to do something with archives produced by its dependencies ( project a having a dependency on project b for such artifacts).

My example would be:

 allprojects {
  
  configurations {
    foo
  }

  task test(type: Tar) {
     inputs.files(configurations.foo) 

     // configure Tar

     doLast {
       // use configurations.foo to access Tar files produced  by task's dependencies
     }
   }

   artifacts {
     foo test
   }
}

project(':a') {
    dependencies {
        foo project(path: ':b', configuration: 'foo')
    }
}

I have not tested this code, but I have implemented this approach on my use-case successfully. I understand now better how it works, thanks for your help!