What if one project needs the artifact produced by another project on its compile classpath?

structuring builds 2

What if it also requires the transitive dependencies of the other project?

This is a common use case for multi-project builds. Gradle offers project dependencies for this.

Depending on another project

A typical multi-project build has the following layout:

.
├── buildSrc
│   ├── src
│   │   └──...
│   └── build.gradle.kts
├── api
│   ├── src
│   │   └──...
│   └── build.gradle.kts
├── services
│   └── person-service
│       ├── src
│       │   └──...
│       └── build.gradle.kts
├── shared
│   ├── src
│   │   └──...
│   └── build.gradle.kts
└── settings.gradle.kts
.
├── buildSrc
│   ├── src
│   │   └──...
│   └── build.gradle
├── api
│   ├── src
│   │   └──...
│   └── build.gradle
├── services
│   └── person-service
│       ├── src
│       │   └──...
│       └── build.gradle
├── shared
│   ├── src
│   │   └──...
│   └── build.gradle
└── settings.gradle

In this example, there are three projects called shared, api, and person-service:

  1. The person-service project depends on the other two projects, shared and api.

  2. The api project depends on the shared project.

  3. Shared build logic used by shared, api, and person-service is provided by buildSrc.

We use the : separator to define a project path such as services:person-service or :shared. Consult the DSL documentation of Settings.include(java.lang.String[]) for more information about defining project paths.

Shared build logic is extracted into a convention plugin in buildSrc that is applied in the subprojects' build scripts that also define project dependencies:

settings.gradle.kts
rootProject.name = "dependencies-java"
include("api", "shared", "services:person-service")
buildSrc/src/main/kotlin/myproject.java-conventions.gradle.kts
plugins {
    id("java")
}

group = "com.example"
version = "1.0"

repositories {
    mavenCentral()
}

dependencies {
    testImplementation("junit:junit:4.13")
}
buildSrc/build.gradle.kts
plugins {
    `kotlin-dsl`
}

repositories {
    mavenCentral()
}
api/build.gradle.kts
plugins {
    id("myproject.java-conventions")
}

dependencies {
    implementation(project(":shared"))
}
shared/build.gradle.kts
plugins {
    id("myproject.java-conventions")
}
services/person-service/build.gradle.kts
plugins {
    id("myproject.java-conventions")
}

dependencies {
    implementation(project(":shared"))
    implementation(project(":api"))
}
settings.gradle
rootProject.name = 'dependencies-java'
include 'api', 'shared', 'services:person-service'
buildSrc/src/main/groovy/myproject.java-conventions.gradle
plugins {
    id 'java'
}

group = 'com.example'
version = '1.0'

repositories {
    mavenCentral()
}

dependencies {
    testImplementation "junit:junit:4.13"
}
buildSrc/build.gradle
plugins {
    id 'groovy-gradle-plugin'
}
api/build.gradle
plugins {
    id 'myproject.java-conventions'
}

dependencies {
    implementation project(':shared')
}
shared/build.gradle
plugins {
    id 'myproject.java-conventions'
}
services/person-service/build.gradle
plugins {
    id 'myproject.java-conventions'
}

dependencies {
    implementation project(':shared')
    implementation project(':api')
}

A project dependency affects execution order. It causes the other project to be built first and adds the output with the classes of the other project to the classpath. It also adds the dependencies of the other project to the classpath.

If you execute gradle :api:compile, first the shared project is built, and then the api project is built.

Project dependencies enable partial multi-project builds.

Depending on artifacts produced by another project

Project dependencies model dependencies between subprojects (modules).

Effectively, a project depends on the main output of another project. In a Java-based project, it’s usually a JAR file.

Sometimes, you may want to depend on an output produced by another task. As such, you want to ensure the task is executed first in order to produce that output. Declaring a task dependency from one project to another is a poor way to model this relationship and introduces unnecessary coupling.

The recommended way to model such a dependency is to produce the output and mark it as an "outgoing" artifact. Gradle’s dependency management engine allows you to share arbitrary artifacts between projects and build them on demand. Consult the Sharing outputs between projects section to learn more.