# Declaring Dependencies between Subprojects

version 7.6.6-20250809031725+0000

## Project dependencies

What if one project needs the jar produced by another project on its compile classpath? What if it also requires the transitive dependencies of the other project? Obviously this is a very common use case for Java multi-project builds. As mentioned in Project dependencies, Gradle offers project dependencies for this.

Example 1. Project dependencies
Project layout

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

Project layout

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

We have the projects `shared`, `api` and `person-service`. The `person-service` project has a dependency on the other two projects. The `api` project has a dependency on the `shared` project. It has no build script and gets nothing injected by another build script. We use the `:` separator to define a project path. Consult the DSL documentation of Settings.include(java.lang.String[]) for more information about defining project paths.

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"
    }

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')
    }

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")
    }

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"))
    }

Shared build logic is extracted into a convention plugin that is applied in the subprojects' build scripts that also define project dependencies. A project dependency is a special form of an execution dependency. It causes the other project to be built first and adds the jar with the classes of the other project to the classpath. It also adds the dependencies of the other project to the classpath. You can trigger a `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 modules. Effectively, you are saying that you depend on the main output of another project. In a Java-based project that’s usually a JAR file.

Sometimes you may want to depend on an output produced by another task. In turn, you’ll want to make sure that the task is executed beforehand to produce that very output. Declaring a task dependency from one project to another is a poor way to model this kind of relationship and introduces unnecessary coupling. The recommended way to model such a dependency is to produce the output, mark it as an "outgoing" artifact or add it to the output of the `main` source set which you can depend on in the consuming project.

Let’s say you are working in a multi-project build with the two subprojects `producer` and `consumer`. The subproject `producer` defines a task named `buildInfo` that generates a properties file containing build information e.g. the project version. You can then map the task provider to its output file and Gradle will automatically establish a task dependency.

Example 2. Task generating a property file containing build information
build.gradle

    plugins {
        id 'java-library'
    }

    version = '1.0'

    def buildInfo = tasks.register("buildInfo", BuildInfo) {
        version = project.version
        outputFile = layout.buildDirectory.file('generated-resources/build-info.properties')
    }

    sourceSets {
        main {
            output.dir(buildInfo.map { it.outputFile.asFile.get().parentFile })
        }
    }

build.gradle.kts

    plugins {
        id("java-library")
    }

    version = "1.0"

    val buildInfo by tasks.registering(BuildInfo::class) {
        version.set(project.version.toString())
        outputFile.set(layout.buildDirectory.file("generated-resources/build-info.properties"))
    }

    sourceSets {
        main {
            output.dir(buildInfo.map { it.outputFile.asFile.get().parentFile })
        }
    }

buildSrc/src/main/java/BuildInfo.java

    public abstract class BuildInfo extends DefaultTask {

        @Input
        public abstract Property<String> getVersion();

        @OutputFile
        public abstract RegularFileProperty getOutputFile();

        @TaskAction
        public void create() throws IOException {
            Properties prop = new Properties();
            prop.setProperty("version", getVersion().get());
            try (OutputStream output = new FileOutputStream(getOutputFile().getAsFile().get())) {
                prop.store(output, null);
            }
        }
    }

The consuming project is supposed to be able to read the properties file at runtime. Declaring a project dependency on the producing project takes care of creating the properties beforehand and making it available to the runtime classpath.

Example 3. Declaring a project dependency on the project producing the properties file
build.gradle

    dependencies {
        runtimeOnly project(':producer')
    }

build.gradle.kts

    dependencies {
        runtimeOnly(project(":producer"))
    }

In the example above, the consumer now declares a dependency on the outputs of the `producer` project.

Depending on the main output artifact from another project is only one example. Gradle has one of the most powerful dependency management engines that allows you to share arbitrary artifacts between projects and let Gradle build them on demand. For more details see the section on sharing outputs between projects.