// Copyright (C) 2023 Gradle, Inc.
//
// Licensed under the Creative Commons Attribution-Noncommercial-ShareAlike 4.0 International License.;
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      https://creativecommons.org/licenses/by-nc-sa/4.0/
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

[[rich-version-constraints]]
= Declaring Rich Versions

Gradle supports a rich model for declaring versions, which allows you to combine different levels of version information.

The terms and their meaning are explained below, from the strongest to the weakest:

[[sec:strict-version]]
`strictly`::
Any version not matched by this version notation will be excluded.
This is the strongest version declaration.
On a declared dependency, a `strictly` can downgrade a version.
For a transitive dependency, dependency resolution will fail if no acceptable version can be selected.
See <<dependency_downgrade_and_exclude.adoc#sec:enforcing_dependency_version,overriding dependency version>> for details.
This term supports dynamic versions.
+
When defined, this overrides any previous `require` declaration and clears previous `reject`.

[[sec:required-version]]
`require`::
Implies that the selected version cannot be lower than what `require` accepts but could be higher through conflict resolution, even if higher has an exclusive higher bound.
This is what a direct dependency translates to.
This term supports dynamic versions.
+
When defined, this overrides any previous `strictly` declaration and clears previous `reject`.

[[sec:preferred-version]]
`prefer`::
This is a very soft version declaration.
It applies only if there is no stronger non-dynamic opinion on a version of the module.
This term does not support dynamic versions.
+
Definition can complement `strictly` or `require`.
+
When defined, this overrides any previous `prefer` declaration and clears previous `reject`.

There is also an additional term outside of the level hierarchy:

[[sec:rejected-version]]
`reject`::
Declares that specific version(s) are not accepted for the module.
This will cause dependency resolution to fail if the selected version is rejected.
This term supports dynamic versions.

The following table illustrates several use cases and how to combine the different terms for rich version declaration:

[%header%autowidth,compact]
|===
| Which version(s) of this dependency are acceptable? | `strictly` | `require` | `prefer` | `rejects` | Selection result

| Tested with version `1.5`; believe all future versions should work.
|
| 1.5
|
|
| Any version starting from `1.5`, equivalent to `org:foo:1.5`. An upgrade to `2.4` is accepted.

| Tested with `1.5`, soft constraint upgrades according to semantic versioning.
|
| [1.0, 2.0[
| 1.5
|
| Any version between `1.0` and `2.0`, `1.5` if nobody else cares. An upgrade to `2.4` is accepted. +
🔒

| Tested with `1.5`, but follows semantic versioning.
| [1.0, 2.0[
|
| 1.5
|
| Any version between `1.0` and `2.0` (exclusive), `1.5` if nobody else cares. +
Overwrites versions from transitive dependencies. +
🔒

| Same as above, with `1.4` known broken.
| [1.0, 2.0[
|
| 1.5
| 1.4
| Any version between `1.0` and `2.0` (exclusive) except for `1.4`, `1.5` if nobody else cares. +
Overwrites versions from transitive dependencies. +
🔒

| No opinion, works with `1.5`.
|
|
| 1.5
|
| `1.5` if no other opinion, any otherwise.

| No opinion, prefer the latest release.
|
|
| `latest.release`
|
| The latest release at build time. +
🔒

| On the edge, latest release, no downgrade.
|
| `latest.release`
|
|
| The latest release at build time. +
🔒

| No other version than 1.5.
| 1.5
|
|
|
| 1.5, or failure if another `strict` or higher `require` constraint disagrees. +
Overwrites versions from transitive dependencies.

| `1.5` or a patch version of it exclusively.
| [1.5,1.6[
|
|
|
| Latest `1.5.x` patch release, or failure if another `strict` or higher `require` constraint disagrees. +
Overwrites versions from transitive dependencies. +
🔒
|===

Lines annotated with a lock (🔒) indicate that leveraging <<dependency_locking.adoc#dependency-locking,dependency locking>> makes sense in this context.
Another concept related to rich version declaration is the ability to publish <<publishing_maven.adoc#publishing_maven:resolved_dependencies,resolved versions>> instead of declared ones.

Using `strictly`, especially for a library, must be a well-thought-out process as it impacts downstream consumers.
At the same time, if used correctly, it will help consumers understand what combination of libraries does not work together in their context.
See <<dependency_downgrade_and_exclude.adoc#sec:enforcing_dependency_version,overriding dependency version>> for more information.

[NOTE]
====
Rich version information will be preserved in the Gradle Module Metadata format.
However conversion to Ivy or Maven metadata formats will be lossy.
The highest level will be published, that is `strictly` or `require` over `prefer`.
In addition, any `reject` will be ignored.
====

Rich version declaration is accessed through the `version` DSL method on a dependency or constraint declaration, which gives access to link:{javadocPath}/org/gradle/api/artifacts/MutableVersionConstraint.html[MutableVersionConstraint]:

====
include::sample[dir="snippets/dependencyManagement/declaringDependencies-concreteVersion/kotlin",files="build.gradle.kts[tags=rich-version]"]
include::sample[dir="snippets/dependencyManagement/declaringDependencies-concreteVersion/groovy",files="build.gradle[tags=rich-version]"]
====
