Declaring Versions and Ranges
The simplest version declaration is a simple string representing the version to use. Gradle supports different ways of declaring a version string:
-
An exact version: e.g.
1.3
,1.3.0-beta3
,1.0-20150201.131010-1
-
A Maven-style version range: e.g.
[1.0,)
,[1.1, 2.0)
,(1.2, 1.5]
-
The
[
and]
symbols indicate an inclusive bound;(
and)
indicate an exclusive bound. -
When the upper or lower bound is missing, the range has no upper or lower bound.
-
The symbol
]
can be used instead of(
for an exclusive lower bound, and[
instead of)
for exclusive upper bound. e.g]1.0, 2.0[
-
An upper bound exclude acts as a prefix exclude. This means that
[1.0, 2.0[
will also exclude all versions starting with2.0
that are smaller than2.0
. For example versions like2.0-dev1
or2.0-SNAPSHOT
are no longer included in the range.
-
-
A prefix version range: e.g.
1.+
,1.3.+
-
Only versions exactly matching the portion before the
+
are included. -
The range
+
on it’s own will include any version.
-
-
A
latest-status
version: e.g.latest.integration
,latest.release
-
Will match the highest versioned module with the specified status. See ComponentMetadata.getStatus().
-
-
A Maven
SNAPSHOT
version identifier: e.g.1.0-SNAPSHOT
,1.4.9-beta1-SNAPSHOT
Version ordering
Versions have an implicit ordering. Version ordering is used to:
-
Determine if a particular version is included in a range.
-
Determine which version is 'newest' when performing conflict resolution.
Versions are ordered based on the following rules:
-
Each version is split into it’s constituent "parts":
-
The characters
[. - _ +]
are used to separate the different "parts" of a version. -
Any part that contains both digits and letters is split into separate parts for each:
1a1 == 1.a.1
-
Only the parts of a version are compared. The actual separator characters are not significant:
1.a.1 == 1-a+1 == 1.a-1 == 1a1
-
-
The equivalent parts of 2 versions are compared using the following rules:
-
If both parts are numeric, the highest numeric value is higher:
1.1
<1.2
-
If one part is numeric, it is considered higher than the non-numeric part:
1.a
<1.1
-
If both are not numeric, the parts are compared alphabetically, case-sensitive:
1.A
<1.B
<1.a
<1.b
-
A version with an extra numeric part is considered higher than a version without:
1.1
<1.1.0
-
A version with an extra non-numeric part is considered lower than a version without:
1.1.a
<1.1
-
-
Certain string values have special meaning for the purposes of ordering:
-
The string
dev
is consider lower than any other string part:1.0-dev
<1.0-alpha
<1.0-rc
. -
The strings
rc
,final
,ga
andrelease
are considered higher than any other string part (sorted in that order):1.0-zeta
<1.0-RC
<1.0-FINAL
<1.0-GA
<1.0-RELEASE
<1.0
. -
The string
SP
will be ordered higher thanrelease
, it remains however lower than an unqualified version, limiting its use to versioning schemes using eitherFINAL
,GA
orRELEASE
:1.0-RELEASE
<1.0-SP1
<1.0
-
The string
snapshot
will be ordered higher thanrc
:1.0-RC
<1.0-SNAPSHOT
<1.0
. -
Numeric snapshot versions have no special meaning, and are sorted like any other numeric part:
1.0
<1.0-20150201.121010-123
<1.1
.
-
Simple version declaration semantics
When you declare a version using the short-hand notation, for example:
dependencies {
implementation('org.slf4j:slf4j-api:1.7.15')
}
dependencies {
implementation("org.slf4j:slf4j-api:1.7.15")
}
Then the version is considered a required version which means that it should minimally be 1.7.15
but can be upgraded by the engine (optimistic upgrade).
There is, however, a shorthand notation for strict versions, using the !!
notation:
dependencies {
// short-hand notation with !!
implementation('org.slf4j:slf4j-api:1.7.15!!')
// is equivalent to
implementation("org.slf4j:slf4j-api") {
version {
strictly '1.7.15'
}
}
// or...
implementation('org.slf4j:slf4j-api:[1.7, 1.8[!!1.7.25')
// is equivalent to
implementation('org.slf4j:slf4j-api') {
version {
strictly '[1.7, 1.8['
prefer '1.7.25'
}
}
}
dependencies {
// short-hand notation with !!
implementation("org.slf4j:slf4j-api:1.7.15!!")
// is equivalent to
implementation("org.slf4j:slf4j-api") {
version {
strictly("1.7.15")
}
}
// or...
implementation("org.slf4j:slf4j-api:[1.7, 1.8[!!1.7.25")
// is equivalent to
implementation("org.slf4j:slf4j-api") {
version {
strictly("[1.7, 1.8[")
prefer("1.7.25")
}
}
}
A strict version cannot be upgraded and overrides whatever transitive dependencies originating from this dependency provide. It is recommended to use ranges for strict versions.
The notation [1.7, 1.8[!!1.7.25
above is equivalent to:
-
strictly
[1.7, 1.8[
-
prefer
1.7.25
which means that the engine must select a version between 1.7 (included) and 1.8 (excluded), and that if no other component in the graph needs a different version, it should prefer 1.7.25
.
Declaring a dependency without version
A recommended practice for larger projects is to declare dependencies without versions and use dependency constraints for version declaration. The advantage is that dependency constraints allow you to manage versions of all dependencies, including transitive ones, in one place.
dependencies {
implementation 'org.springframework:spring-web'
}
dependencies {
constraints {
implementation 'org.springframework:spring-web:5.0.2.RELEASE'
}
}
dependencies {
implementation("org.springframework:spring-web")
}
dependencies {
constraints {
implementation("org.springframework:spring-web:5.0.2.RELEASE")
}
}