Gradle 5.x requires listing a lot more dependencies than 4.x

We are upgrading projects from the latest 4.x to the latest 5.x. The upgrade process went fine until we built, at which point we got a ton of compiler errors about classes not found. We could see the jars that contain the classes in the dependencies tree, but without adding a number of jars we couldn’t make the errors go away.

Does 5.x fine-grained dependency analysis perhaps wind up generating a smaller tree of dependencies, thereby requiring that more “tops” of the forest be listed?

It didn’t even add dependencies that were in the same tree. For example, one of our jars, let’s call it foo depends on another of our jars, bar. We had foo in the dependencies() section, but didn’t list bar, because in 4.x it automatically figured out that bar was needed by foo. But in 5.x we had to list both foo and bar. We can flip gradlew back and force and reproduce the behavior.

One big change in Gradle 5.0 was the separation of compile and runtime scoped dependencies.

In earlier versions of Gradle, a transitive dependency declared in a POM with runtime scope would be included in the compile classpath. In 5.0 this has changed: only compile scope dependencies are included in the compilation classpath. Dependencies with runtime scope are included in the runtime classpath.

I suspect that this is what’s causing your compilation errors. You can use a Gradle Build Scan or the dependencies report to compare the compileClasspath and runtimeClasspath between the 2 Gradle versions.

Every dependecy we have is in compile, except one runtime scoped. Would you expect to see much of a change in that case?

Yes, there can still be a big difference in what gets resolved into your compileClasspath, because dependencies can be published with compile or runtime scope in a Maven POM file.

Let’s say your build declares a dependency like compile 'org.example:moduleX:1.0, and that moduleX is published to mavenCentral with a POM containing something like:

    <dependencies>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.0</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.0</version>
            <scope>runtime</scope>
        </dependency>

In Gradle 4.x, your compileClasspath would contain moduleX.jar, slf4j-api.jar AND slf4j-log4j12.jar. So you’d get your declared dependency, as well as any transitive dependencies with scope == runtime or scope == compile.

In Gradle 5.x, your compileClasspath will contain moduleX.jar and slf4j-api.jar BUT NOT slf4j-log4j12.jar. This is because the last one is declared in a POM with scope == runtime, and Gradle will no longer include that when compiling.

So if your code is referencing types from slf4j-log4j12.jar, then it will compile successfully with Gradle 4.x, but will fail to compile with Gradle 5.x.

That worked great, except now the generated POM is empty.

What’s the trick to getting “api” dependencies to show up in the POM?

Without knowing how you’re publishing, it’s difficult to provide help on this.

Can you describe what you have changed that has caused this change in the generated POM? Is this simply a factor of moving from Gradle 4.x to 5.x, or could it be due to some other change you made in the process?

We had to include the java plugin. Then everything worked.