Declaring Dependency Constraints (original) (raw)

Version declarations (including ranges) only affect the dependencies you declare directly. To control versions of transitive dependencies, use dependency constraints.

dependency management constraints

A dependency constraint sets version requirements for a module without adding that module as a dependency. When the module is pulled in, the constraint participates in version conflict resolution just like a declared dependency version:

| | Use constraints when you want to control versions centrally and avoid adding extra dependencies just to force a version. | | --------------------------------------------------------------------------------------------------------------------------- |

You can declare constraints:

build.gradle.kts

plugins {
    `java-platform`
}

dependencies {
    constraints {
        // Platform declares some versions of libraries used in subprojects
        api("commons-httpclient:commons-httpclient:3.1")
        api("org.apache.commons:commons-lang3:3.8.1")
    }
}

build.gradle

plugins {
    id 'java-platform'
}

dependencies {
    constraints {
        // Platform declares some versions of libraries used in subprojects
        api 'commons-httpclient:commons-httpclient:3.1'
        api 'org.apache.commons:commons-lang3:3.8.1'
    }
}

Declaring constraints alongside direct dependencies

Constraints are scoped by bucket configurations (e.g., implementation, runtimeOnly). They apply whenever that dependency is encountered during resolution.

The constraints {} block is used within the dependencies {} block to declare these constraints:

build.gradle.kts

plugins {
    `java-platform`
}

dependencies {
    constraints {
        api("commons-httpclient:commons-httpclient:3.1")
        runtime("org.postgresql:postgresql:42.2.5")
    }
}

build.gradle

plugins {
    id 'java-platform'
}

dependencies {
    constraints {
        api 'commons-httpclient:commons-httpclient:3.1'
        runtime 'org.postgresql:postgresql:42.2.5'
    }
}
  1. api("commons-httpclient:commons-httpclient:3.1"): ensures ≥ 3.1 for api.
  2. runtime("org.postgresql:postgresql:42.2.5"): ensures ≥ 42.2.5 for runtime.

If multiple requirements exist, Gradle picks a version that satisfies all. If none exists, resolution fails with an error describing the conflict.

Adding constraints on transitive dependencies

Use constraints to select transitive modules without introducing them as direct dependencies:

build.gradle.kts

dependencies {
    implementation("org.apache.httpcomponents:httpclient")
    constraints {
        implementation("org.apache.httpcomponents:httpclient:4.5.3") {
            because("previous versions have a bug impacting this application")
        }
        implementation("commons-codec:commons-codec:1.11") {
            because("version 1.9 pulled from httpclient has bugs affecting this application")
        }
    }
}

build.gradle

dependencies {
    implementation('org.apache.httpcomponents:httpclient')
    constraints {
        implementation('org.apache.httpcomponents:httpclient:4.5.3') {
            because('previous versions have a bug impacting this application')
        }
        implementation('commons-codec:commons-codec:1.11') {
            because('version 1.9 pulled from httpclient has bugs affecting this application')
        }
    }
}

If commons-codec isn’t brought in transitively, the constraint is a no-op (it doesn’t add the module). If it is brought in, the constraint guides the version selection.

Rich versions and strict versions for constraints

build.gradle.kts

dependencies {
    constraints {
        implementation("com.google.guava:guava") {
            version {
                strictly("33.1.0-jre")
            }
            because("avoid older versions with known issues")
        }
    }
}

build.gradle

dependencies {
    constraints {
        implementation("com.google.guava:guava") {
            version {
                strictly("33.1.0-jre")
            }
            because("avoid older versions with known issues")
        }
    }
}

Transitivity of constraints

Dependency constraints are transitive.

If library A depends on library B, and library B declares a constraint on module C, that constraint will affect the version of module C that library A depends on.

For example, if library A depends on module C version 2, but library B declares a constraint on module C version 3, library A will resolve version 3 of module C.

Publishing constraints

Dependency constraints are only published when using Gradle Module Metadata. This means they are fully supported only when both publishing and consuming modules with Gradle.

If modules are consumed with Maven or Ivy, the constraints may not be preserved.