Ivy: Conflicts & Versions
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
This page is about what Ivy 2.6.0 does when its graph contains the same module twice at different revisions, and about the many ways a dependency can ask for a
revision without naming one. It is the fourth of six Ivy pages. How modules are found is in
Settings & Resolvers; the descriptor elements used here (dependency, exclude, override, configurations) are
described in Module Descriptors & Configurations; the tasks that print the
reports are in Ivy Ant Tasks; deliver and publish are in
Ivy: Publishing & Multi-Project Builds. The language-neutral ideas (nearest versus highest, ranges, pinning) are in
Dependency Management Fundamentals.
How the examples were run. Every example was executed on 2026-10-16 with Apache Ant 1.10.18, Apache Ivy 2.6.0 and JDK 26 on macOS, each in its own scratch directory with its own Ivy
cache and started with ant -lib lib -Divy.default.ivy.user.dir=<scratch>/build/ivy-user (the lib directory holds the Ivy JAR and the Apache ORO JAR). Some use Maven Central, with real Apache
Commons coordinates; the others use small local file repositories of made-up tidepool modules, written by a helper script that creates an ivy.xml and a JAR, so that the
versions are exactly the ones the text needs. Output is shown as printed, with the scratch directory shortened to .. Left out are the Buildfile: and target lines, the Ivy
banner line, the :: resolving dependencies :: line, the progress dots and the downloading and [SUCCESSFUL ] lines (except where a download is the point), BUILD SUCCESSFUL and the
Total time line; nothing else is edited. Runs against Maven Central were made from an empty cache unless the text says otherwise. Maven Central was reached over HTTPS; no credentials were used anywhere.
Anything that could not be run is labelled not run here.
What counts as a conflict
Ivy calls two revisions of the same module in conflict. "Same module" means the same organisation and module name couple. A conflict is found while Ivy walks the graph, in the order in which dependencies are declared. Each time it meets a module it already knows, it asks the conflict manager for that module which revisions to keep. The ones not kept are evicted. Because this happens during the resolve, an evicted module is never downloaded, and if it has not been seen yet its descriptor is not fetched either: Ivy tries to evict "as soon as possible", which saves whole sub-trees, and that is why the order of dependencies matters for download volume.
The default conflict manager is latest-revision. It can be changed for everything (defaultConflictManager on the settings element), for a set of modules (conflict-manager on a module rule
in modules), or for one module inside a descriptor (the conflict element). The settings and descriptor routes are shown below.
Source: Ivy documentation: conflict
The conflict managers
| Name | What it keeps |
|---|---|
|
The latest revision, by comparing the revision strings with the |
|
The latest revision by publication date. The reference warns that it is costly to compute and prefers |
|
The latest revision that can still give a compatible set of dependencies according to the version constraints in the graph. It does not allow unresolvable conflicts (like |
|
Nothing: any conflict makes the resolve fail. |
|
The revision nearest to the root of the dependency graph, which is how Maven handles conflicts. |
|
Everything. Also called the no conflict manager: nothing is evicted and both revisions end up in the configuration. |
The two "latest" managers (latest-revision and latest-time) also take the force attribute of a direct dependency into account, described below. Three more types exist for building your own in a conflict-managers element:
latest-cm (a latest strategy of your choice), compatible-cm (the same for the compatible variant) and regexp-cm, which fails when two revisions in conflict have different values for a
regular-expression group, for instance when (.*)\.\d sees 1.2.x and 1.3.y. These were not run here. The 2.6.0 release notes list nearest as a new feature ("handles conflicts the same way that Maven does").
Source: Ivy 2.6.0 release notes
A conflict to experiment with
The scenario uses real artifacts from Maven Central. commons-csv 1.14.1 depends on commons-io 2.20.0 and commons-codec 1.19.0. The tidepool descriptor also declares
commons-io directly, at an older revision, 2.15.1:
<ivy-module version="2.0">
<info organisation="io.tidepool" module="tidepool-engine" revision="1.0.0-SNAPSHOT"/>
<configurations>
<conf name="runtime"/>
</configurations>
<dependencies defaultconfmapping="runtime->default">
<dependency org="org.apache.commons" name="commons-csv" rev="1.14.1"/>
<dependency org="commons-io" name="commons-io" rev="2.15.1"/>
</dependencies>
</ivy-module>
The settings choose the conflict manager from a variable, so every run below changes only -Dcm=...:
<ivysettings>
<property name="cm" value="latest-revision" override="false"/>
<settings defaultResolver="central" defaultConflictManager="${cm}"/>
<caches defaultCacheDir="${basedir}/build/cache"/>
<resolvers>
<ibiblio name="central" m2compatible="true"/>
</resolvers>
</ivysettings>
<project name="conflict-demo" default="resolve" xmlns:ivy="antlib:org.apache.ivy.ant">
<property name="dep.file" value="ivy.xml"/>
<ivy:settings file="${basedir}/ivysettings.xml"/>
<target name="resolve">
<ivy:resolve file="${dep.file}"/>
<ivy:cachepath pathid="cp" conf="runtime" log="quiet"/>
<pathconvert property="cp.str" refid="cp" pathsep="${line.separator}">
<mapper type="flatten"/>
</pathconvert>
<echo>${cp.str}</echo>
</target>
<target name="report" depends="resolve">
<ivy:report todir="${basedir}/build/report" graph="false" conf="runtime" outputpattern="[organisation]-[module]-[conf].[ext]" xml="true" xsl="false"/>
<ivy:dependencytree conf="runtime"/>
</target>
</project>
The files in this section are this page’s own setup for the examples that follow, not taken from the documentation.
latest-revision, the default
With nothing configured, commons-io 2.20.0 (from commons-csv) beats 2.15.1 (the direct dependency), because 2.20.0 is the latest. The 2.15.1 module is listed as evicted, and its
descriptor was never looked up: search is 3 for four modules, and only three JARs are on the classpath.
[ivy:resolve] :: loading settings :: file = ./ivysettings.xml
[ivy:resolve] confs: [runtime]
[ivy:resolve] found org.apache.commons#commons-csv;1.14.1 in central
[ivy:resolve] found commons-io#commons-io;2.20.0 in central
[ivy:resolve] found commons-codec#commons-codec;1.19.0 in central
[ivy:resolve] :: resolution report :: resolve 1506ms :: artifacts dl 382ms
[ivy:resolve] :: evicted modules:
[ivy:resolve] commons-io#commons-io;2.15.1 by [commons-io#commons-io;2.20.0] in [runtime]
---------------------------------------------------------------------
| | modules || artifacts |
| conf | number| search|dwnlded|evicted|| number|dwnlded|
---------------------------------------------------------------------
| runtime | 4 | 3 | 3 | 1 || 3 | 3 |
---------------------------------------------------------------------
[echo] commons-csv-1.14.1.jar
[echo] commons-io-2.20.0.jar
[echo] commons-codec-1.19.0.jar
The reading guide for this output: :: evicted modules: names the loser, the winner and the configuration; the evicted column counts losers; modules number counts all of them, including the evicted one;
artifacts number counts only what will be used. The classpath printed at the end has the winners only.
The conflict goes in an unexpected direction for people who think "I declared 2.15.1 myself, so I get 2.15.1". That is what nearest and force are for.
nearest (since 2.6.0)
nearest takes the revision with the smallest distance from the root of the graph. A direct dependency is nearer than a transitive one, so the declared 2.15.1 wins and 2.20.0 is
evicted, which is exactly what Maven would do:
[ivy:resolve] :: loading settings :: file = ./ivysettings.xml
[ivy:resolve] confs: [runtime]
[ivy:resolve] found org.apache.commons#commons-csv;1.14.1 in central
[ivy:resolve] found commons-codec#commons-codec;1.19.0 in central
[ivy:resolve] found commons-io#commons-io;2.15.1 in central
[ivy:resolve] :: resolution report :: resolve 2072ms :: artifacts dl 391ms
[ivy:resolve] :: evicted modules:
[ivy:resolve] commons-io#commons-io;2.20.0 by [commons-io#commons-io;2.15.1] in [runtime]
---------------------------------------------------------------------
| | modules || artifacts |
| conf | number| search|dwnlded|evicted|| number|dwnlded|
---------------------------------------------------------------------
| runtime | 4 | 3 | 3 | 1 || 3 | 3 |
---------------------------------------------------------------------
[echo] commons-csv-1.14.1.jar
[echo] commons-codec-1.19.0.jar
[echo] commons-io-2.15.1.jar
When two revisions are at the same distance, the declaration order decides in Maven. In the run on ranges further down, where both revisions are two steps from the root, nearest kept the one that was
reached first.
strict
strict is the way to find out where the conflicts are: the resolve fails and names both sides and who wanted them.
[ivy:resolve] :: loading settings :: file = ./ivysettings.xml
[ivy:resolve] confs: [runtime]
[ivy:resolve] found org.apache.commons#commons-csv;1.14.1 in central
[ivy:resolve] :: problems summary ::
[ivy:resolve] :::: ERRORS
[ivy:resolve] commons-io#commons-io;2.20.0 (needed by [org.apache.commons#commons-csv;1.14.1]) conflicts with commons-io#commons-io;2.15.1 (needed by [io.tidepool#tidepool-engine;1.0.0-SNAPSHOT])
[ivy:resolve] :: USE VERBOSE OR DEBUG MESSAGE LEVEL FOR MORE DETAILS
BUILD FAILED
./build.xml:5: impossible to resolve dependencies:
commons-io#commons-io;2.20.0 (needed by [org.apache.commons#commons-csv;1.14.1]) conflicts with commons-io#commons-io;2.15.1 (needed by [io.tidepool#tidepool-engine;1.0.0-SNAPSHOT])
The same rule exists in the other tools as a check rather than as a resolution rule: the Maven Enforcer’s dependencyConvergence and Gradle’s failOnVersionConflict()
(see the table at the end of this page).
all
all keeps both. Nothing is evicted, and both commons-io JARs are on the classpath, which is almost never what you want in a plain Ant classpath. It is useful when each revision is meant to be
loaded by a separate class loader, as the concept page says.
[ivy:resolve] :: loading settings :: file = ./ivysettings.xml
[ivy:resolve] confs: [runtime]
[ivy:resolve] found org.apache.commons#commons-csv;1.14.1 in central
[ivy:resolve] found commons-io#commons-io;2.20.0 in central
[ivy:resolve] found commons-codec#commons-codec;1.19.0 in central
[ivy:resolve] found commons-io#commons-io;2.15.1 in central
[ivy:resolve] :: resolution report :: resolve 2447ms :: artifacts dl 617ms
---------------------------------------------------------------------
| | modules || artifacts |
| conf | number| search|dwnlded|evicted|| number|dwnlded|
---------------------------------------------------------------------
| runtime | 4 | 4 | 4 | 0 || 4 | 4 |
---------------------------------------------------------------------
[echo] commons-csv-1.14.1.jar
[echo] commons-io-2.20.0.jar
[echo] commons-codec-1.19.0.jar
[echo] commons-io-2.15.1.jar
latest-time, and the cache effect
latest-time decides by publication date, which Ivy must obtain for each candidate. The same descriptor was resolved twice with -Dcm=latest-time, the first time from an empty cache and the second
time with the cache of the first. The two results differ.
First, from an empty cache. Both revisions are kept, nothing is evicted:
[ivy:resolve] :: loading settings :: file = ./ivysettings.xml
[ivy:resolve] confs: [runtime]
[ivy:resolve] found org.apache.commons#commons-csv;1.14.1 in central
[ivy:resolve] found commons-io#commons-io;2.20.0 in central
[ivy:resolve] found commons-codec#commons-codec;1.19.0 in central
[ivy:resolve] found commons-io#commons-io;2.15.1 in central
[ivy:resolve] :: resolution report :: resolve 2333ms :: artifacts dl 569ms
---------------------------------------------------------------------
| | modules || artifacts |
| conf | number| search|dwnlded|evicted|| number|dwnlded|
---------------------------------------------------------------------
| runtime | 4 | 4 | 4 | 0 || 4 | 4 |
---------------------------------------------------------------------
[echo] commons-csv-1.14.1.jar
[echo] commons-io-2.20.0.jar
[echo] commons-codec-1.19.0.jar
[echo] commons-io-2.15.1.jar
Second, the very same resolve with the cache of the first run. Now 2.15.1 is evicted:
[ivy:resolve] :: loading settings :: file = ./ivysettings.xml
[ivy:resolve] confs: [runtime]
[ivy:resolve] found org.apache.commons#commons-csv;1.14.1 in central
[ivy:resolve] found commons-io#commons-io;2.20.0 in central
[ivy:resolve] found commons-codec#commons-codec;1.19.0 in central
[ivy:resolve] found commons-io#commons-io;2.15.1 in central
[ivy:resolve] :: resolution report :: resolve 47ms :: artifacts dl 2ms
[ivy:resolve] :: evicted modules:
[ivy:resolve] commons-io#commons-io;2.15.1 by [commons-io#commons-io;2.20.0] in [runtime]
---------------------------------------------------------------------
| | modules || artifacts |
| conf | number| search|dwnlded|evicted|| number|dwnlded|
---------------------------------------------------------------------
| runtime | 4 | 0 | 0 | 1 || 3 | 0 |
---------------------------------------------------------------------
[echo] commons-csv-1.14.1.jar
[echo] commons-io-2.20.0.jar
[echo] commons-codec-1.19.0.jar
The first answer is a result that depends on the state of the cache. The reason was not investigated here; it is consistent with publication dates being unavailable for a module whose descriptor
has not been downloaded yet. Whatever the cause, a build whose classpath changes between a clean checkout and a second run is a build to avoid, and latest-revision does not behave that way.
Use latest-time only where revision strings really cannot be ordered, and then make a clean build the thing you check.
latest-compatible, and why it exists
In the commons-io scenario the two versions are exact, so there is no set of versions that satisfies both, and latest-compatible fails just like strict:
[ivy:resolve] :: loading settings :: file = ./ivysettings.xml
[ivy:resolve] confs: [runtime]
[ivy:resolve] found org.apache.commons#commons-csv;1.14.1 in central
[ivy:resolve] :: problems summary ::
[ivy:resolve] :::: ERRORS
[ivy:resolve] commons-io#commons-io;2.20.0 (needed by [org.apache.commons#commons-csv;1.14.1]) conflicts with commons-io#commons-io;2.15.1 (needed by [io.tidepool#tidepool-engine;1.0.0-SNAPSHOT])
[ivy:resolve] :: USE VERBOSE OR DEBUG MESSAGE LEVEL FOR MORE DETAILS
BUILD FAILED
./build.xml:5: impossible to resolve dependencies:
commons-io#commons-io;2.20.0 (needed by [org.apache.commons#commons-csv;1.14.1]) conflicts with commons-io#commons-io;2.15.1 (needed by [io.tidepool#tidepool-engine;1.0.0-SNAPSHOT])
It earns its place with ranges. The scenario is a local repository with tidepool-core in five revisions, and two modules that need it: tidepool-model accepts any
1.x below 2.0 and tidepool-engine accepts only 1.2.0 to 1.5.0.
tidepool-core: 1.0.0 1.2.0 1.4.0 1.6.0 2.1.0
tidepool-model 1.0.0 requires tidepool-core [1.0,2.0[
tidepool-engine 1.0.0 requires tidepool-core [1.2.0,1.5.0]
The dependencies in the descriptors are <dependency org="io.tidepool" name="tidepool-core" rev="[1.0,2.0[" conf="compile->default"/> (in tidepool-model) and the same with
rev="[1.2.0,1.5.0]" (in tidepool-engine). The consumer, tidepool-cli, depends on both, tidepool-model first:
<ivy-module version="2.0">
<info organisation="io.tidepool" module="tidepool-cli" revision="1.0.0-SNAPSHOT"/>
<configurations>
<conf name="default"/>
</configurations>
<dependencies>
<dependency org="io.tidepool" name="tidepool-model" rev="1.0.0" conf="default->default"/>
<dependency org="io.tidepool" name="tidepool-engine" rev="1.0.0" conf="default->default"/>
</dependencies>
</ivy-module>
With latest-revision Ivy resolves [1.0,2.0[ to 1.6.0, then meets [1.2.0,1.5.0], which would give 1.4.0, and evicts 1.4.0 in favour of 1.6.0. The result violates the range that tidepool-engine
declared:
[ivy:resolve] :: loading settings :: file = ./ivysettings.xml
[ivy:resolve] confs: [default]
[ivy:resolve] found io.tidepool#tidepool-model;1.0.0 in local
[ivy:resolve] found io.tidepool#tidepool-core;1.6.0 in local
[ivy:resolve] [1.6.0] io.tidepool#tidepool-core;[1.0,2.0[
[ivy:resolve] found io.tidepool#tidepool-engine;1.0.0 in local
[ivy:resolve] found io.tidepool#tidepool-core;1.4.0 in local
[ivy:resolve] [1.4.0] io.tidepool#tidepool-core;[1.2.0,1.5.0]
[ivy:resolve] :: resolution report :: resolve 56ms :: artifacts dl 4ms
[ivy:resolve] :: evicted modules:
[ivy:resolve] io.tidepool#tidepool-core;1.4.0 by [io.tidepool#tidepool-core;1.6.0] in [default]
---------------------------------------------------------------------
| | modules || artifacts |
| conf | number| search|dwnlded|evicted|| number|dwnlded|
---------------------------------------------------------------------
| default | 4 | 4 | 4 | 1 || 3 | 3 |
---------------------------------------------------------------------
With latest-compatible Ivy notices that 1.6.0 cannot satisfy tidepool-engine, blacklists it for that situation, and ends with 1.4.0, the latest revision inside both ranges. Note the line that starts with BLACKLISTING (long, and
printed as one line):
[ivy:resolve] :: loading settings :: file = ./ivysettings.xml
[ivy:resolve] confs: [default]
[ivy:resolve] found io.tidepool#tidepool-model;1.0.0 in local
[ivy:resolve] found io.tidepool#tidepool-core;1.6.0 in local
[ivy:resolve] [1.6.0] io.tidepool#tidepool-core;[1.0,2.0[
[ivy:resolve] found io.tidepool#tidepool-engine;1.0.0 in local
[ivy:resolve] BLACKLISTING [io.tidepool#tidepool-core;1.6.0 blacklisted to evict io.tidepool#tidepool-core;1.6.0 in favor of io.tidepool#tidepool-core;[1.2.0,1.5.0] in io.tidepool#tidepool-cli;1.0.0-SNAPSHOT for default]
[ivy:resolve] found io.tidepool#tidepool-core;1.4.0 in local
[ivy:resolve] [1.4.0] io.tidepool#tidepool-core;[1.0,2.0[
[ivy:resolve] :: resolution report :: resolve 53ms :: artifacts dl 4ms
[ivy:resolve] :: evicted modules:
[ivy:resolve] io.tidepool#tidepool-core;[1.2.0,1.5.0] by [io.tidepool#tidepool-core;1.4.0] in [default]
---------------------------------------------------------------------
| | modules || artifacts |
| conf | number| search|dwnlded|evicted|| number|dwnlded|
---------------------------------------------------------------------
| default | 4 | 3 | 3 | 1 || 3 | 3 |
---------------------------------------------------------------------
strict fails here as well:
[ivy:resolve] :: loading settings :: file = ./ivysettings.xml
[ivy:resolve] confs: [default]
[ivy:resolve] found io.tidepool#tidepool-model;1.0.0 in local
[ivy:resolve] found io.tidepool#tidepool-core;1.6.0 in local
[ivy:resolve] [1.6.0] io.tidepool#tidepool-core;[1.0,2.0[
[ivy:resolve] found io.tidepool#tidepool-engine;1.0.0 in local
[ivy:resolve] found io.tidepool#tidepool-core;1.4.0 in local
[ivy:resolve] [1.4.0] io.tidepool#tidepool-core;[1.2.0,1.5.0]
[ivy:resolve] :: problems summary ::
[ivy:resolve] :::: ERRORS
[ivy:resolve] io.tidepool#tidepool-core;1.4.0 (needed by [io.tidepool#tidepool-engine;1.0.0]) conflicts with io.tidepool#tidepool-core;1.6.0 (needed by [io.tidepool#tidepool-model;1.0.0])
[ivy:resolve] :: USE VERBOSE OR DEBUG MESSAGE LEVEL FOR MORE DETAILS
BUILD FAILED
./build.xml:4: impossible to resolve dependencies:
io.tidepool#tidepool-core;1.4.0 (needed by [io.tidepool#tidepool-engine;1.0.0]) conflicts with io.tidepool#tidepool-core;1.6.0 (needed by [io.tidepool#tidepool-model;1.0.0])
And nearest keeps the first one met, 1.6.0, again ignoring the range of the second:
[ivy:resolve] :: loading settings :: file = ./ivysettings.xml
[ivy:resolve] confs: [default]
[ivy:resolve] found io.tidepool#tidepool-model;1.0.0 in local
[ivy:resolve] found io.tidepool#tidepool-core;1.6.0 in local
[ivy:resolve] [1.6.0] io.tidepool#tidepool-core;[1.0,2.0[
[ivy:resolve] found io.tidepool#tidepool-engine;1.0.0 in local
[ivy:resolve] :: resolution report :: resolve 40ms :: artifacts dl 4ms
[ivy:resolve] :: evicted modules:
[ivy:resolve] io.tidepool#tidepool-core;[1.2.0,1.5.0] by [io.tidepool#tidepool-core;1.6.0] in [default]
---------------------------------------------------------------------
| | modules || artifacts |
| conf | number| search|dwnlded|evicted|| number|dwnlded|
---------------------------------------------------------------------
| default | 4 | 3 | 3 | 1 || 3 | 3 |
---------------------------------------------------------------------
The lesson is the same in every tool. If a module states a range, only a resolution rule that reads ranges (here latest-compatible, in Gradle the rich-version constraints) honours it; a rule that only
compares the winners does not.
Choosing the manager: settings, rules and descriptors
The same choice can be made at three levels, from the widest to the narrowest:
-
All modules.
<settings defaultConflictManager="nearest"/>, as in the runs above (through a variable). -
A set of modules. In settings,
<modules><module organisation="apache" name="ivy*" matcher="glob" conflict-manager="latest-time"/></modules>(since 1.4, from themodulereference). Not run here. -
One module in one descriptor. The
conflictelement ofivy.xml(since 2.0):org,moduleandmatcherpick the modules (default*andexact), and eithermanagernames a manager orrevlists revisions, which creates a fixed conflict manager. Theconflictscontainer withmanagerchildren is the old form, deprecated since 2.0.
The fixed form is the strictest: whatever the graph asks for, the listed revision is the one. With <conflict org="commons-io" module="commons-io" rev="2.15.1"/> after the dependencies:
<ivy-module version="2.0">
<info organisation="io.tidepool" module="tidepool-engine" revision="1.0.0-SNAPSHOT"/>
<configurations>
<conf name="runtime"/>
</configurations>
<dependencies defaultconfmapping="runtime->default">
<dependency org="org.apache.commons" name="commons-csv" rev="1.14.1"/>
<dependency org="commons-io" name="commons-io" rev="2.15.1"/>
<conflict org="commons-io" module="commons-io" rev="2.15.1"/>
</dependencies>
</ivy-module>
[ivy:resolve] :: loading settings :: file = ./ivysettings.xml
[ivy:resolve] confs: [runtime]
[ivy:resolve] found org.apache.commons#commons-csv;1.14.1 in central
[ivy:resolve] found commons-codec#commons-codec;1.19.0 in central
[ivy:resolve] found commons-io#commons-io;2.15.1 in central
[ivy:resolve] :: resolution report :: resolve 2430ms :: artifacts dl 482ms
[ivy:resolve] :: evicted modules:
[ivy:resolve] commons-io#commons-io;2.20.0 by [] ([fixed[2.15.1]]) in [runtime]
---------------------------------------------------------------------
| | modules || artifacts |
| conf | number| search|dwnlded|evicted|| number|dwnlded|
---------------------------------------------------------------------
| runtime | 4 | 3 | 3 | 1 || 3 | 3 |
---------------------------------------------------------------------
[echo] commons-csv-1.14.1.jar
[echo] commons-codec-1.19.0.jar
[echo] commons-io-2.15.1.jar
The report line by [] ([fixed[2.15.1]]) is how a fixed conflict manager shows itself: there is no winner module in square brackets, only the rule.
Source: Ivy documentation: conflict
force, override and exclude
A conflict manager is a rule that is applied to a conflict. Three descriptor features change the graph before or around the rule.
force
force="true" on a direct dependency tells the two "latest" managers to prefer this revision to the ones brought in by other dependencies. With the dependency
<dependency org="commons-io" name="commons-io" rev="2.15.1" force="true"/> and the default manager, the declared revision beats the transitive 2.20.0:
<ivy-module version="2.0">
<info organisation="io.tidepool" module="tidepool-engine" revision="1.0.0-SNAPSHOT"/>
<configurations>
<conf name="runtime"/>
</configurations>
<dependencies defaultconfmapping="runtime->default">
<dependency org="org.apache.commons" name="commons-csv" rev="1.14.1"/>
<dependency org="commons-io" name="commons-io" rev="2.15.1" force="true"/>
</dependencies>
</ivy-module>
[ivy:resolve] :: loading settings :: file = ./ivysettings.xml
[ivy:resolve] confs: [runtime]
[ivy:resolve] found org.apache.commons#commons-csv;1.14.1 in central
[ivy:resolve] found commons-codec#commons-codec;1.19.0 in central
[ivy:resolve] found commons-io#commons-io;2.15.1 in central
[ivy:resolve] :: resolution report :: resolve 2159ms :: artifacts dl 429ms
[ivy:resolve] :: evicted modules:
[ivy:resolve] commons-io#commons-io;2.20.0 by [commons-io#commons-io;2.15.1] in [runtime]
---------------------------------------------------------------------
| | modules || artifacts |
| conf | number| search|dwnlded|evicted|| number|dwnlded|
---------------------------------------------------------------------
| runtime | 4 | 3 | 3 | 1 || 3 | 3 |
---------------------------------------------------------------------
[echo] commons-csv-1.14.1.jar
[echo] commons-codec-1.19.0.jar
[echo] commons-io-2.15.1.jar
force is documented for the two "latest" managers (latest-revision and latest-time). With nearest it is not needed, because a direct dependency is already the nearest.
override
override replaces the revision or branch of a transitive dependency without declaring a dependency on it and without a conflict. It is the Ivy counterpart of dependencyManagement. The
reference says it is applied "before any else", in the dependency descriptor mediation phase, so the module then behaves as if it had been declared with the new value. Here commons-io is not a dependency of the
project at all, but 2.18.0 is imposed on the one commons-csv brings. Neither 2.15.1 nor 2.20.0 is ever looked up:
<ivy-module version="2.0">
<info organisation="io.tidepool" module="tidepool-engine" revision="1.0.0-SNAPSHOT"/>
<configurations>
<conf name="runtime"/>
</configurations>
<dependencies defaultconfmapping="runtime->default">
<dependency org="org.apache.commons" name="commons-csv" rev="1.14.1"/>
<override org="commons-io" module="commons-io" rev="2.18.0"/>
</dependencies>
</ivy-module>
[ivy:resolve] :: loading settings :: file = ./ivysettings.xml
[ivy:resolve] confs: [runtime]
[ivy:resolve] found org.apache.commons#commons-csv;1.14.1 in central
[ivy:resolve] found commons-io#commons-io;2.18.0 in central
[ivy:resolve] found commons-codec#commons-codec;1.19.0 in central
[ivy:resolve] :: resolution report :: resolve 2048ms :: artifacts dl 418ms
---------------------------------------------------------------------
| | modules || artifacts |
| conf | number| search|dwnlded|evicted|| number|dwnlded|
---------------------------------------------------------------------
| runtime | 3 | 3 | 3 | 0 || 3 | 3 |
---------------------------------------------------------------------
[echo] commons-csv-1.14.1.jar
[echo] commons-io-2.18.0.jar
[echo] commons-codec-1.19.0.jar
Only commons-io 2.18.0 is found; no eviction is reported, because there was no conflict. In 2.6.0 override is also understood by ivy:retrieve and the post-resolve tasks (IVY-1664), and
ivy:makepom writes the overrides of an ivy.xml into the dependencyManagement of the POM it generates (IVY-1663).
Source: Ivy documentation: override
exclude
exclude removes a module (or artifacts) from the graph. Written inside a dependency it applies below that dependency; written directly under dependencies (since 2.0) it applies to the whole module. Excluding a
module (since 1.3) means that it is not downloaded at all and its own dependencies are not looked at; excluding only artifacts still resolves the module. The attributes are org, module, artifact/name, type, ext, matcher and conf. The exclusion below sits inside
the commons-csv dependency:
<ivy-module version="2.0">
<info organisation="io.tidepool" module="tidepool-engine" revision="1.0.0-SNAPSHOT"/>
<configurations>
<conf name="runtime"/>
</configurations>
<dependencies defaultconfmapping="runtime->default">
<dependency org="org.apache.commons" name="commons-csv" rev="1.14.1">
<exclude org="commons-codec" module="commons-codec"/>
</dependency>
</dependencies>
</ivy-module>
[ivy:resolve] :: loading settings :: file = ./ivysettings.xml
[ivy:resolve] confs: [runtime]
[ivy:resolve] found org.apache.commons#commons-csv;1.14.1 in central
[ivy:resolve] found commons-io#commons-io;2.20.0 in central
[ivy:resolve] :: resolution report :: resolve 1542ms :: artifacts dl 282ms
---------------------------------------------------------------------
| | modules || artifacts |
| conf | number| search|dwnlded|evicted|| number|dwnlded|
---------------------------------------------------------------------
| runtime | 2 | 2 | 2 | 0 || 2 | 2 |
---------------------------------------------------------------------
[echo] commons-csv-1.14.1.jar
[echo] commons-io-2.20.0.jar
There is no commons-codec in the module count, in the classpath or in the report. Excluding a module that something else needs compiles and then fails at run time (the classic
NoClassDefFoundError), so an exclusion belongs with a comment saying why. transitive="false" on a dependency is the other tool for the same job: take this module but none of its dependencies. It was not run here, and neither was the
module-wide exclude. The default matcher of exclude is exact for Ivy files of version 1.3 and later; a glob matcher needs Apache ORO, see the Settings & Resolvers page for the error you get without it.
Source: Ivy documentation: exclude
Reading evictions
Evictions can be seen in four places, from quick to detailed:
-
The resolve log: the
:: evicted modules:block and theevictedcolumn of the table, shown above. -
ivy:dependencytree, an indented tree of what was selected. The evicted revision does not appear: the directcommons-iodependency of the project is shown with the winning revision, 2.20.0, and so is the one undercommons-csv. -
The XML report that
ivy:reportwrites, or the resolution cache file<organisation>-<module>-<conf>.xml. Eachmodulehasrevisionchildren and the loser carriesevictedandevicted-by. -
The HTML report from the same task, with a graph option, which is the form to look at when there are dozens of evictions. It was not run here.
The dependency tree and the lines of the XML report for the default latest-revision run:
[ivy:dependencytree] Dependency tree for io.tidepool-tidepool-engine
[ivy:dependencytree] +- org.apache.commons#commons-csv;1.14.1
[ivy:dependencytree] | +- commons-io#commons-io;2.20.0
[ivy:dependencytree] \- commons-codec#commons-codec;1.19.0
[ivy:dependencytree] \- commons-io#commons-io;2.20.0
The tree’s own formatting is as printed: the | after the first child is missing where a continuation line should be. The XML report (which xsl="false" kept as plain XML) names the cause for the evicted revision:
<revision name="2.15.1" evicted="latest-revision" evicted-reason="" downloaded="false" searched="false" conf="default, compile, runtime, master" position="-1">
<evicted-by rev="2.20.0"/>
Read it as: revision 2.15.1 of commons-io was evicted by the latest-revision manager, evicted-by 2.20.0, and was never downloaded or searched. The two caller elements of the surviving revision (not shown) list who asked for what, which is the
information needed to decide whether to add force, override or exclude. The ivy:dependencytree and ivy:report tasks are described, with their attributes, on
Ivy Ant Tasks; the build file above has the report target that ran them.
Source: Ivy documentation: report
Revisions: fixed, dynamic and special
A dependency has a rev attribute. It can be fixed (1.5.2) or dynamic, and revConstraint (since 2.0) records the original dynamic constraint next to a resolved one. The built-in version matchers
are:
| Revision | Matches |
|---|---|
|
Exactly that revision (the exact matcher, always present). |
|
The latest revision of the module, whatever its status. |
|
The latest revision that has at least that status (since 1.4). The statuses are |
|
Every revision starting with the prefix: 1.0.1 and 1.0.5 match |
|
At least 1.0 and at most 2.0. |
|
At least 1.0 and below 2.0. |
|
Above 1.0 and at most 2.0. |
|
Above 1.0 and below 2.0. |
|
1.0 or above; above 1.0. |
|
Up to and including 2.0; below 2.0. |
|
Whatever a |
a Maven timestamped snapshot such as |
Handled by the |
Which revision is the "latest" among the candidates is a separate question, answered by the latest strategy of the resolver (below); and for any dynamic form the list of candidates has to come from the resolver, so
the resolver must be able to list revisions (the ibiblio resolver reads maven-metadata.xml; a filesystem or url pattern needs [revision] in it).
Dynamic revisions in practice
Five forms against Maven Central, each with a descriptor holding one dependency on commons-codec. The rev attribute is the only line that changes:
<ivy-module version="2.0">
<info organisation="io.tidepool" module="tidepool-engine" revision="1.0.0-SNAPSHOT"/>
<configurations>
<conf name="runtime"/>
</configurations>
<dependencies defaultconfmapping="runtime->master">
<dependency org="commons-codec" name="commons-codec" rev="REV"/>
</dependencies>
</ivy-module>
Each run printed the line that starts with the resolved revision in brackets and the classpath entry:
### rev="1.16.+"
[ivy:resolve] [1.16.1] commons-codec#commons-codec;1.16.+
[echo] commons-codec-1.16.1.jar
### rev="[1.15,1.17["
[ivy:resolve] [1.16.1] commons-codec#commons-codec;[1.15,1.17[
[echo] commons-codec-1.16.1.jar
### rev="(,1.16.0]"
[ivy:resolve] [1.16.0] commons-codec#commons-codec;(,1.16.0]
[echo] commons-codec-1.16.0.jar
### rev="latest.release"
[ivy:resolve] [1.23.0] commons-codec#commons-codec;latest.release
[echo] commons-codec-1.23.0.jar
### rev="latest.integration"
[ivy:resolve] [1.23.0] commons-codec#commons-codec;latest.integration
[echo] commons-codec-1.23.0.jar
1.16.+ took 1.16.1, the highest 1.16 patch release. [1.15,1.17[ also gave 1.16.1. (,1.16.0] gave 1.16.0, the highest revision at or below the bound. latest.release and
latest.integration both gave 1.23.0, the newest of the module at the time of the run (2026-10-16). A build file that names these forms gets a different JAR when Maven Central gets a new release; that is the
point of the feature and the reason to pin the result with deliver (below) when a build must be repeatable. The result of a dynamic revision is also cached for a time; the TTL section of
Settings & Resolvers shows a resolve that did not notice a new revision for that reason, and refresh on ivy:resolve as the way
out.
Latest strategies
Wherever Ivy needs to know which of two revisions is later, it asks a latest strategy, set on the resolver with latest or globally with defaultLatestStrategy. There are three built in:
-
latest-revision, the default, compares the revisions as strings in a way close to PHP’sversion_compare, and knows some words:1.0-dev1is before1.0-alpha1, before1.0-rc1, before1.0, before1.0.1. The words and their weights are configurable (alatest-revisionelement inlatest-strategieswithspecialMeaningchildren and optionallyusedefaultspecialmeanings="false"); the defaults named aredev(-1),rc(1) andfinal(2). Not run here. -
latest-lexicocompares the strings with Java’s string comparison, so 1.9 is after 1.10. -
latest-timecompares the date, which for afilesystemresolver is the last-modified time of the descriptor and for remote ones needs a request per candidate.
A repository with the revisions 1.9, 1.10, 1.10-rc1 and 1.10-dev1, whose descriptors were given increasing modification times in that order of creation (1.9 oldest, 1.10-dev1 newest) with touch, and a dependency on latest.integration:
1.10
1.10-dev1
1.10-rc1
1.9
The only thing that changes between the runs is the latest attribute of the resolver (<filesystem name="local" latest="${strategy}">):
### latest=latest-revision
[ivy:resolve] found io.tidepool#tidepool-model;1.10 in local
[ivy:resolve] [1.10] io.tidepool#tidepool-model;latest.integration
### latest=latest-lexico
[ivy:resolve] found io.tidepool#tidepool-model;1.9 in local
[ivy:resolve] [1.9] io.tidepool#tidepool-model;latest.integration
### latest=latest-time
[ivy:resolve] found io.tidepool#tidepool-model;1.10-dev1 in local
[ivy:resolve] [1.10-dev1] io.tidepool#tidepool-model;latest.integration
latest-revision picks 1.10, latest-lexico 1.9, and latest-time the one touched last, 1.10-dev1, a development build, because the time of publication has nothing to do with the version number. A repository that
publishes development and final versions into the same module needs the revision strategy, or a statuses-based latest.release.
Changing revisions and snapshots
By default Ivy assumes that a revision never changes: once a descriptor and its artifacts are in the cache, the cache is trusted and the repository is not asked again. The Maven habit of republishing one version, a
-SNAPSHOT, violates that. Ivy supports it with the notion of a changing revision, declared per dependency (changing="true") or for all matching revisions on a resolver with changingPattern and changingMatcher. For
changing revisions Ivy compares the publication date of the descriptor with the cached one and, if it moved, checks the artifacts. For changed descriptors the resolver attribute checkmodified="true" makes Ivy compare the descriptor’s last-modified date. The reference
recommends publishing with ivy:publish, which updates the publication date; hand-copied files do not.
The scenario publishes tidepool-model 1.0-SNAPSHOT three times (the JAR contains a text file that says which build it is, about.txt), changing the publication attribute of the descriptor each time, and resolves with
different combinations of the dependency attribute and the resolver attribute. The settings turn checkmodified on or off through a variable:
<ivysettings>
<property name="check" value="false" override="false"/>
<settings defaultResolver="snapshots"/>
<caches defaultCacheDir="${basedir}/build/cache"/>
<resolvers>
<filesystem name="snapshots" checkmodified="${check}">
<ivy pattern="${basedir}/repo/[organisation]/[module]/[revision]/ivy.xml"/>
<artifact pattern="${basedir}/repo/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]"/>
</filesystem>
</resolvers>
</ivysettings>
<ivy-module version="2.0">
<info organisation="io.tidepool" module="tidepool-engine" revision="1.0.0-SNAPSHOT"/>
<configurations>
<conf name="default"/>
</configurations>
<dependencies>
<dependency org="io.tidepool" name="tidepool-model" rev="1.0-SNAPSHOT" changing="true" conf="default->default"/>
</dependencies>
</ivy-module>
<project name="changing-demo" default="resolve" xmlns:ivy="antlib:org.apache.ivy.ant">
<property name="dep.file" value="ivy-changing-false.xml"/>
<ivy:settings file="${basedir}/ivysettings.xml"/>
<target name="resolve">
<ivy:resolve file="${dep.file}"/>
<ivy:cachepath pathid="cp" log="quiet"/>
<property name="jar" refid="cp"/>
<exec executable="unzip"><arg line="-p ${jar} about.txt"/></exec>
</target>
</project>
Scenario A. After the third build is published, only the dependency says changing="true" and the resolver does not check modification:
### 1: build 1 is published (changing=true, checkmodified=true)
[ivy:resolve] found io.tidepool#tidepool-model;1.0-SNAPSHOT in snapshots
[ivy:resolve] downloading ./repo/io.tidepool/tidepool-model/1.0-SNAPSHOT/tidepool-model-1.0-SNAPSHOT.jar ...
[ivy:resolve] [SUCCESSFUL ] io.tidepool#tidepool-model;1.0-SNAPSHOT!tidepool-model.jar (2ms)
[exec] io.tidepool#tidepool-model;1.0-SNAPSHOT build 1
### 2: build 2 is published (changing=false, checkmodified=false)
[ivy:resolve] found io.tidepool#tidepool-model;1.0-SNAPSHOT in snapshots
[exec] io.tidepool#tidepool-model;1.0-SNAPSHOT build 1
### 3: same repository (changing=true, checkmodified=true)
[ivy:resolve] found io.tidepool#tidepool-model;1.0-SNAPSHOT in snapshots
[ivy:resolve] downloading ./repo/io.tidepool/tidepool-model/1.0-SNAPSHOT/tidepool-model-1.0-SNAPSHOT.jar ...
[ivy:resolve] [SUCCESSFUL ] io.tidepool#tidepool-model;1.0-SNAPSHOT!tidepool-model.jar (1ms)
[exec] io.tidepool#tidepool-model;1.0-SNAPSHOT build 2
### 4: build 3 is published (changing=true, checkmodified=false)
[ivy:resolve] found io.tidepool#tidepool-model;1.0-SNAPSHOT in snapshots
[ivy:resolve] downloading ./repo/io.tidepool/tidepool-model/1.0-SNAPSHOT/tidepool-model-1.0-SNAPSHOT.jar ...
[ivy:resolve] [SUCCESSFUL ] io.tidepool#tidepool-model;1.0-SNAPSHOT!tidepool-model.jar (1ms)
[exec] io.tidepool#tidepool-model;1.0-SNAPSHOT build 3
Step 2 shows the default behaviour: build 2 was published, and the resolve with changing="false" and checkmodified="false" found the module in the cache and kept build 1. Step 3, with both turned on, downloaded build 2. In step 4 changing="true"
alone was enough to pick up build 3, with a fresh publication date in the descriptor.
Scenario B is the same until build 3 is published, then tries other combinations in a different order:
### 1: build 1 is published (changing=true, checkmodified=true)
[ivy:resolve] found io.tidepool#tidepool-model;1.0-SNAPSHOT in snapshots
[ivy:resolve] downloading ./repo/io.tidepool/tidepool-model/1.0-SNAPSHOT/tidepool-model-1.0-SNAPSHOT.jar ...
[ivy:resolve] [SUCCESSFUL ] io.tidepool#tidepool-model;1.0-SNAPSHOT!tidepool-model.jar (1ms)
[exec] io.tidepool#tidepool-model;1.0-SNAPSHOT build 1
### 2: build 2 is published (changing=false, checkmodified=false)
[ivy:resolve] found io.tidepool#tidepool-model;1.0-SNAPSHOT in snapshots
[exec] io.tidepool#tidepool-model;1.0-SNAPSHOT build 1
### 3: same repository (changing=true, checkmodified=true)
[ivy:resolve] found io.tidepool#tidepool-model;1.0-SNAPSHOT in snapshots
[ivy:resolve] downloading ./repo/io.tidepool/tidepool-model/1.0-SNAPSHOT/tidepool-model-1.0-SNAPSHOT.jar ...
[ivy:resolve] [SUCCESSFUL ] io.tidepool#tidepool-model;1.0-SNAPSHOT!tidepool-model.jar (1ms)
[exec] io.tidepool#tidepool-model;1.0-SNAPSHOT build 2
### 4: build 3 is published (changing=false, checkmodified=true)
[ivy:resolve] found io.tidepool#tidepool-model;1.0-SNAPSHOT in snapshots
[exec] io.tidepool#tidepool-model;1.0-SNAPSHOT build 2
### 5: same repository (changing=true, checkmodified=false)
[ivy:resolve] found io.tidepool#tidepool-model;1.0-SNAPSHOT in snapshots
[exec] io.tidepool#tidepool-model;1.0-SNAPSHOT build 2
### 6: same repository (changing=true, checkmodified=true)
[ivy:resolve] found io.tidepool#tidepool-model;1.0-SNAPSHOT in snapshots
[exec] io.tidepool#tidepool-model;1.0-SNAPSHOT build 2
Here nothing ever got build 3. Step 4 had checkmodified="true" but no changing: Ivy refreshed the descriptor in the cache (the cached publication attribute has the new value) but not the JAR, and from then on the metadata looked current,
so the later changing="true" runs saw nothing to update. The evidence from the cache and the repository after step 6:
repository: publication="20261016120000"
cache: publication="20261016120000"
The reference describes the logic: if the metadata of a module has not changed, Ivy considers the whole module unchanged, and only when the publication date moved does it look at the artifacts, and only for changing revisions. A resolve that sees the new descriptor
but does not treat the revision as changing uses up that signal. The practical rules from the two scenarios: declare snapshots as changing="true" (or with a changingPattern) before the first time any build resolves them; do not use checkmodified on a resolver for revisions that are not declared changing; and if the cache has got into the state of scenario B, delete the module from the cache or run cleancache (see
Ivy Ant Tasks).
The Ivy documentation prefers the other way: give every integration build its own revision number or timestamp and ask for latest.integration. That makes builds reproducible, and allows promotion of a build to a milestone or a release without
rebuilding it. The cost is clean-up of the intermediate revisions in the repository.
deliver: from dynamic to fixed
Dynamic revisions are for development. A module that is published should say exactly what it was built against. ivy:deliver writes a copy of the descriptor in which every dynamic revision is replaced by the one that resolved, and keeps the
original constraint in revConstraint. Using the repository of the latest-strategy test (default strategy), the deliver target of the build resolves and then delivers as revision 1.0.0:
<target name="deliver" depends="resolve">
<ivy:deliver deliverpattern="${basedir}/build/delivered/[artifact]-[revision].[ext]" pubrevision="1.0.0" status="release"/>
<concat><fileset dir="${basedir}/build/delivered" includes="ivy-*.xml"/></concat>
</target>
deliver:
[ivy:deliver] :: delivering :: io.tidepool#tidepool-engine;1.0.0-SNAPSHOT :: 1.0.0 :: release :: Fri Oct 16 12:22:01 CEST 2026
[ivy:deliver] delivering ivy file to ./build/delivered/ivy-1.0.0.xml
[concat] <?xml version="1.0" encoding="UTF-8"?>
[concat] <ivy-module version="2.0">
[concat] <info organisation="io.tidepool" module="tidepool-engine" revision="1.0.0" status="release" publication="20261016122201"/>
[concat] <configurations>
[concat] <conf name="default"/>
[concat] </configurations>
[concat] <dependencies>
[concat] <dependency org="io.tidepool" name="tidepool-model" rev="1.10" revConstraint="latest.integration" conf="default->default"/>
[concat] </dependencies>
[concat] </ivy-module>
rev="1.10" is what resolved, revConstraint="latest.integration" is what was asked. In 2.6.0 deliver replaces dynamic revisions with the revision resolved before any conflict resolution, as it did before Ivy 2.3 (IVY-1485, IVY-1661), so that the
delivered descriptor reproduces the resolve even with several configurations, and it also replaces dynamic revisions inherited from a parent descriptor (IVY-1410). The release notes advise removing the Ivy cache after upgrading from an older Ivy, because some fixes only apply to descriptors parsed afresh. deliver and publish in full are on
Ivy: Publishing & Multi-Project Builds.
Source: Ivy documentation: deliver
Circular dependencies
A cycle (A needs B and B needs A) cannot be sorted into a build order, but Ivy can still resolve it. What it does is a setting, circularDependencyStrategy on the settings element (since 1.4): warn (default) prints a warning, ignore is silent, and error
fails. A local repository where tidepool-model 1.0.0 depends on tidepool-engine 1.0.0 and the other way round, resolved from tidepool-cli, with the strategy as the only variable:
### circularDependencyStrategy=warn
[ivy:resolve] :: loading settings :: file = ./ivysettings.xml
[ivy:resolve] confs: [default]
[ivy:resolve] found io.tidepool#tidepool-engine;1.0.0 in local
[ivy:resolve] found io.tidepool#tidepool-model;1.0.0 in local
[ivy:resolve] :: resolution report :: resolve 32ms :: artifacts dl 4ms
---------------------------------------------------------------------
| | modules || artifacts |
| conf | number| search|dwnlded|evicted|| number|dwnlded|
---------------------------------------------------------------------
| default | 2 | 2 | 2 | 0 || 2 | 2 |
---------------------------------------------------------------------
[ivy:resolve] :: problems summary ::
[ivy:resolve] :::: WARNINGS
[ivy:resolve] circular dependency found: io.tidepool#tidepool-engine;1.0.0->io.tidepool#tidepool-model;1.0.0->...
[ivy:resolve] circular dependency found: io.tidepool#tidepool-model;1.0.0->io.tidepool#tidepool-engine;1.0.0->...
[ivy:resolve] :: USE VERBOSE OR DEBUG MESSAGE LEVEL FOR MORE DETAILS
### circularDependencyStrategy=error
[ivy:resolve] :: loading settings :: file = ./ivysettings.xml
[ivy:resolve] confs: [default]
[ivy:resolve] found io.tidepool#tidepool-engine;1.0.0 in local
[ivy:resolve] found io.tidepool#tidepool-model;1.0.0 in local
[ivy:resolve] :: problems summary ::
[ivy:resolve] :::: ERRORS
[ivy:resolve] io.tidepool#tidepool-engine;1.0.0->io.tidepool#tidepool-model;1.0.0->...
[ivy:resolve] :: USE VERBOSE OR DEBUG MESSAGE LEVEL FOR MORE DETAILS
BUILD FAILED
./build.xml:4: impossible to resolve dependencies:
org.apache.ivy.plugins.circular.CircularDependencyException: io.tidepool#tidepool-engine;1.0.0->io.tidepool#tidepool-model;1.0.0->...
### circularDependencyStrategy=ignore
[ivy:resolve] :: loading settings :: file = ./ivysettings.xml
[ivy:resolve] confs: [default]
[ivy:resolve] found io.tidepool#tidepool-engine;1.0.0 in local
[ivy:resolve] found io.tidepool#tidepool-model;1.0.0 in local
[ivy:resolve] :: resolution report :: resolve 31ms :: artifacts dl 4ms
---------------------------------------------------------------------
| | modules || artifacts |
| conf | number| search|dwnlded|evicted|| number|dwnlded|
---------------------------------------------------------------------
| default | 2 | 2 | 2 | 0 || 2 | 2 |
---------------------------------------------------------------------
(The Java stack trace that follows the error failure was cut from the listing after the exception message.) With warn, the cycle is reported twice, once from each side, under WARNINGS; with ignore the summary is silent;
with error the build stops with a CircularDependencyException. A cycle between modules is a design problem more often than a repository problem.
The same ideas in Maven and Gradle
| Topic | Ivy | Maven | Gradle |
|---|---|---|---|
Default winner |
|
Nearest definition; the first declaration wins a tie |
The highest version, unless a stricter constraint says otherwise |
Fail on a conflict |
|
Enforcer |
|
Pin a version |
|
A direct declaration, or |
|
Ranges |
|
|
|
Snapshots |
|
|
|
Make dynamic versions reproducible |
|
Avoid ranges and dynamic versions; pin them in the POM |
Dependency locking |
Detail and the commands are on Maven dependencies, scopes and mediation, Maven versions, SNAPSHOTs and ranges, Gradle dependency resolution, conflicts and capabilities and Gradle dependency locking, verification and caching. The Maven Resolver Ant Tasks apply Maven’s nearest rule without any of the settings above; see Maven Resolver Ant Tasks.
Common problems
-
The version I declared is not the one on the classpath. The default
latest-revisionmanager kept a higher one from a transitive dependency. Read:: evicted modules:; fix withforce="true"(if you need the lower one),override, a fixedconflict, or switch tonearest. -
A range in a dependency is ignored. A manager that compares winners (
latest-revision,nearest) can select a revision outside the range of another module; the 1.6.0 against[1.2.0,1.5.0]run above. Uselatest-compatiblewhen ranges are in play. -
Different classpaths on a clean machine and on a developer machine. Check
latest-time; its result depended on the cache in the run above. Also check dynamic revisions and their TTL. -
A snapshot does not update. The dependency lacks
changing="true"and the resolver has no matchingchangingPattern; or the cache was refreshed by a resolve that checked the descriptor only (scenario B). Fix the declaration and clean the cache. -
Both versions of a library on the classpath. The
allmanager is in force, either as the default or for that module, or the two revisions have different organisations (renamed artifacts, such as the move fromcommons-langtoorg.apache.commons:commons-lang3, are different modules and never conflict). -
circular dependency foundwarnings. Redesign the modules; the strategy only chooses whether to complain. -
latest.releaseis slow. Ivy has to read descriptors from the newest revision downwards until it finds the status. Prefer ranges or a pinned revision on large repositories. -
The glob matcher fails to load. Apache ORO is missing; see Settings & Resolvers.
Where the books and older build files are outdated
-
Material written before Ivy 2.6.0 cannot mention the
nearestconflict manager. It makes the Maven rule available in Ivy, so a statement that Ivy cannot resolve conflicts the way Maven does is out of date. -
The
conflictselement and itsmanagerchildren are deprecated since Ivy 2.0 in favour ofconflict, the form used in these examples. -
Material on dynamic revisions written before Ivy 2.0 does not mention
revConstraintor the resolve modes; adeliver`d descriptor from 2.x has both `revandrevConstraint. Some examples in the reference spell the attributerevision; the attribute table ofdependencylistsrev. -
From Ivy 2.3.0 to 2.5.x,
deliverreplaced dynamic revisions with the revision selected after conflict resolution, which could differ per configuration; 2.6.0 returns to the behaviour before 2.3.0 (IVY-1485, IVY-1661). -
Any statement that
latest.integrationgives repeatable builds is wrong for a repository where new revisions can appear; the result of a dynamic revision is only stable inside its TTL (10 seconds by default).
References
-
Apache Ivy documentation (2.6.0): conflict-managers
-
Apache Ivy documentation (2.6.0): dependency
-
Apache Ivy documentation (2.6.0): version-matchers
-
Apache Ivy documentation (2.6.0): latest-strategies
-
Apache Ivy documentation (2.6.0): Main concepts
-
Apache Ivy documentation (2.6.0): Best practices
-
Apache Ivy documentation (2.6.0): deliver
-
Apache Ivy documentation (2.6.0): Release notes