What dynamic version selectors does Gradle support, and how does it choose which concrete version to resolve?
answer
- prefix / range / latest.* / +
- bracket inclusive, paren exclusive
- list → filter → status → highest
- version ordering: 1.10 > 1.9, rc < release
- componentSelection can reject candidates
basics
~10 sGradle supports prefix wildcards ('1.+'), Maven ranges ('[1.0,2.0)'), and the keywords 'latest.release' / 'latest.integration'. It lists candidate versions, filters by the selector and component status, then picks the highest match.
solid answer
~50 sGradle accepts several dynamic selectors: - **Prefix:** `'1.+'`, `'1.2.+'` — highest version with that prefix. - **Maven ranges:** `'[1.0,2.0)'`, `'(,1.5]'`, `'[2.0,)'` — bracket = inclusive, paren = exclusive. - **latest selectors:** `'latest.release'` (highest version whose status is `release`), `'latest.integration'` (highest of any status, includes snapshots). `'+'` means anything. To resolve, Gradle gathers the candidate version list from repository metadata (Maven `maven-metadata.xml` or Ivy directory listings / module metadata), filters to those matching the selector, then applies its **version ordering** (a semantic-ish comparison where, e.g., `1.10 > 1.9` and qualifiers like `rc` sort below releases). The highest surviving candidate wins. `latest.release` additionally checks the component's declared **status** (from Ivy/Gradle Module Metadata) and rejects non-release statuses. Because this depends on what's published *now*, results can drift, so dynamic selectors should be paired with dependency locking for reproducibility.
code
groovy · 5 linesdependencies {
implementation 'org.example:lib:1.+'
implementation 'org.example:api:[2.0,3.0)'
implementation 'org.example:tool:latest.release'
}go deeper
Recognise the common selectors ('1.+', ranges, latest.release).
Explain the list→filter→pick-highest flow and the inclusive/exclusive bracket semantics.
Discuss version ordering nuances, status filtering, componentSelection vetoes, and interaction with conflict resolution.
Mandate failOnDynamicVersions plus locking org-wide; treat dynamic selectors as a dev-time convenience only.
## The selector grammar Gradle understands these dynamic forms: | Selector | Meaning | |---|---| | `1.+` / `1.2.+` | Prefix match — highest version starting with the prefix | | `[1.0,2.0)` | Range, 1.0 inclusive to 2.0 exclusive | | `(1.0,2.0)` | Both exclusive | | `[1.0,]` / `[1.0,)` | 1.0 and anything higher | | `(,2.0)` | Anything below 2.0 | | `latest.release` | Highest version with status `release` | | `latest.integration` | Highest version of any status (incl. snapshots) | | `+` | Absolute highest version (discouraged) | ## How resolution works 1. **List candidates.** Gradle reads each repository's version listing — for Maven that's `maven-metadata.xml`; for Ivy it can be a directory listing or metadata. The dynamic-versions cache (default 24h) governs how often this listing is re-fetched. 2. **Filter by selector.** Keep only versions matching the range/prefix/keyword. 3. **Filter by status (latest.* only).** `latest.release` keeps versions whose component status is `release`. Status comes from Gradle Module Metadata or Ivy descriptors; plain Maven modules default to `release` for final versions and `integration` for snapshots. 4. **Order and pick the highest.** Gradle's version comparator splits versions into numeric and non-numeric parts: numeric parts compare numerically (`1.10 > 1.9`), and special qualifiers are ranked so that `1.0 > 1.0-rc > 1.0-beta > 1.0-alpha` and dev/snapshot qualifiers sort below releases. ## Interaction with conflict resolution A resolved dynamic version still participates in Gradle's graph conflict resolution: if another path forces a higher static version, the higher one may win. You can also constrain or reject candidates with **rich versions** (`require`, `prefer`, `strictly`, `reject`) on a sibling concern, and a `componentSelection` rule can veto specific candidates: ```kotlin configurations.all { resolutionStrategy.componentSelection { all { if (candidate.version.contains("beta")) reject("no betas") } } } ``` ## Reproducibility Because step 1 depends on what is published at build time, the same source can resolve differently over time. The standard mitigation is **dependency locking** — resolve once, write `gradle.lockfile`, and have subsequent builds verify against it. `failOnDynamicVersions()` / `failOnChangingVersions()` can additionally make builds fail fast if such inputs slip in.
- How does 'latest.release' differ from 'latest.integration'?`latest.release` only considers versions whose component status is `release`; `latest.integration` considers any status, so it can pick up snapshots/integration builds.
- Does '1.+' resolve to 1.9 or 1.10 if both exist?1.10. Gradle's version ordering compares numeric segments numerically, so 1.10 ranks above 1.9.
- How can you exclude pre-release candidates from a dynamic resolution?Add a `componentSelection` rule that rejects candidates whose version contains qualifiers like `rc`/`beta`, or use rich-version `reject`.
saying these in an interview costs you the question
- Assuming '1.+' resolves to 1.9 over 1.10 (string compare instead of numeric).
- Thinking brackets and parentheses are interchangeable in ranges.
- Saying latest.release includes snapshots.