Why does a compile project dependency use the 'default' configuration by default?

(Shin Tai) #1

If I have project A that depends on project B, Gradle will use the ‘default’ configuration of B to compile A. This means that A’s compile classpath will contain B.jar and any of B’s runtime dependencies.

Does it make sense to anyone else why you’d want to compile against another project’s runtime dependencies and why Gradle does this by default?

I’m seeing a real case of when the project is imported into IntelliJ, classes are not being found because IntelliJ is building its classpath differently. It seems to think that only the compile configuration is visible - which seems sensible to me. Of course, now that intellij is highlighting these ‘problems’, I can add those dependencies explicitly; but Gradle’s not going to care.

So it feels like that if I try to do the right thing (if this is even the right thing to do) by explicitly declaring my compile dependencies, Gradle’s not going to help me out by failing when I mess up.

I think the behaviour I’m after would mean doing something like:

dependencies {
  compile project(path: ":B", configuration: "compile")
  runtime project(path: ":B", configuration: "runtime")

But because this isn’t the default Gradle behaviour, it’s making me doubt what I’m trying to do.


(Luke Daley) #2

Hi Shin,

This is how Gradle implements at this time. The new work we are doing on publications and components is the long road to fixing this behaviour. In the past we have found that the more restrictive project dependencies WRT transitives caused issues, because the Gradle mechanism was not flexible enough.

A project dependency is a dependency on the “default” configuration of the other project. By default, this extends the “archives” (the thing(s) being produced) and the “runtime” configuration. If you want to separate these things out, on the provider side you’ll need:

configurations {
  compileProject.extendsFrom compile, archives
  runtimeProject.extendsFrom runtime, archives

The on the consumer:

dependencies {
  compile project(path: ":B", configuration: "compileProject")
  runtime project(path: ":B", configuration: "runtimeProject")

There are a few contributing factors here. The most significant of which is that the compile/runtime separation mechanism is domain specific, while the project dependency mechanism is general. Because Gradle supports more than the Java domain, we can’t encode this compile/runtime split in the base mechanism.

Going forward, project dependencies will be more targeted. In that you’ll depend on a publication/component of a providing project. This is a richer datatype that actually describes the thing. The other side is rule based contextual dependency consumption. These two things combined will allow for smarter consumption of dependencies in different contexts which is what the runtime/compile issue comes down to.

While it’s technically correct to keep runtime deps off the compile classpath (and preferable, no argument there from me) in practice this has proven to be not as big an issue as it may appear. That said, we are working towards a general solution.