This document describes the rest of the requirements for dependency management that have not yet been implemented for Maven 2.0, especially with regards to transitive dependencies.
The Dependency Management element
This is inherited, and gives a full profile over your dependency set. While it will not add new dependencies itself, it will enforce the following constraints:
- your policy on the version allowed for a dependency
- affects both your declared dependencies and transitive dependencies
- apply a specific scope to a dependency
Resolve Dependencies once only
This is a relatively straightforward task in the resolver:
- eg if maven-artifact is in the top level pom, go through the tree and find all the references to it, decide the version, and then use that version's dependencies, not the others
- this would probably be done with a DAG using just group/artifact ID with counts on the edges. When a version is chosen all the other versions are removed, and when a count drops to 0 it is removed from the graph. Proceed until all versions are determined.
- this ensures the pool of dependencies is smaller when trying to determine the version to use, and much more accurate.
Dependency Version Ranges
Need to be able to declare minimum, maximum allowed versions of a dependency (both min and max may be optional), and allow "holes" for known incompatible versions.
x <= 1.0
"Soft" requirement on 1.0 (just a recommendation - helps select the correct version if it matches all ranges)
Hard requirement on 1.0
1.2 <= x <= 1.3
1.0 <= x < 2.0
x >= 1.5
x <= 1.0 or x >= 1.2. This excludes 1.1 (Multiple sets are comma-separated)
Mathematical syntax chosen to avoid the use of
- as it would conflict with what is used in many version number.
Of the overlapping ranges, the highest soft requirement is the version to be used in the default strategy. If there are no soft requirements inside the prescribed ranges, the RELEASE version is used. If that does not fit the described ranges, then the uppermost found version number in the prescribed ranges is used. If the ranges exclude all versions, an error occurs.
Addition of ranges leads to additional necessary specifications on the dependency element.
Version comparison requirement
The effectiveness of the above ranges reqires that we are able to compare two versions and determine which is newer. This should be designed such that it is pluggable, but initially we should only provide one scheme and attempt to use it uniformly.
Default Version comparison definition
The default specification should be composed as follows:
- the qualifier section is optional (and is SNAPSHOT, alpha-1, alpha-2)
- the build section is optional (and increments starting at 1 if specified)
- any '0' build or revision elements can be omitted.
- _ is used for build to avoid confusion (eg alpha-1 only is a release with no build number)
For ordering, the following is done in order until an element is found that are not equal:
- numerical comparison of major version
- numerical comparison of minor version
- if revision does not exist, add ".0" for comparison purposes
- numerical comparison of revision
- if qualifier does not exist, it is newer than if it does
- case-insensitive string comparison of qualifier
- this ensures timestamps are correctly ordered, and SNAPSHOT is newer than an equivalent timestamp
- this also ensures that beta comes after alpha, as does rc
- if build does not exist, add "_0" for comparison purposes
- numerical comparison of build
The resolved version element should be added to the dependency element, in both the
dependencies list and the
dependencyManagement. When present, it specifies what version was resolved at release time. It is not intended for entry by users. Its benefit is that you are able to retain both the dependency version specification, as well as discovering the reproducible equivalent.
An alternative I considered was to have
version behave as it is now, and introduce
versionSpec which would give the ranges. However, the above solution is better in the following ways:
- it will also apply to
- it allows existing dependencies to work as soft references without modification
Given that an open ended version specification such as
[1.0,) will use a
RELEASE version dependency if available, there is no reason to allow and resolve such a version for a dependency.
Version specifications for Parents
Parent references are handled differently to dependencies. They do not need to incorporate ranges, and are generally treated as unversioned. See the Release Management document for a discussion of this.
SNAPSHOT versions into the specification
Resolution of dependency ranges should not resolve to a snapshot unless it is included as an explicit boundary. There is no need to compile against development code unless you are explicitly using a new feature, under which the snapshot will become the lower bound of your version specification. As releases are considered newer than the snapshot they belong to, they will be chosen over an old snapshot if found.
It is possible that applications such as Continuum may have a mode that enables always resolving to the snapshot version, but this is external to the POM itself.
Forcing a version
A version will always be honoured if it is declared in the current POM with a hard requirement on a particular version - however, it should be noted that this will also affect other poms downstream if it is itself depended on using transitive dependencies.
The preferred technique for forcing a particular version to be used should then be the use of the
It is important that we are able to easily and clearly represent the state of a projects dependency tree both from m2 and graphical tools given the added complexity of transitive dependencies and version management. The diagnostics should as much as possible explain why a version was chosen.
Strategies for conflict resolution need to be pluggable and stackable.
In some cases, the resolution of a version may be workable, but not ideal. For example instead of erroring out, we may just drop to a different method of conflict resolution if there is no match in the first method (ending up using the current nearest wins algorithm).
Some suggested techniques would be:
- use version specification (as defined above, default)
- use nearest (always get closest transitive dep regardless of version specifications)
- fail if no match found