How does maven sort version numbers?
Asked Answered
G

3

37

Maven seems to have the ability to indicate a range of versions such as <version>[1.2.3,)</version> how does maven figure out what is a newer or older version when there is no consistent versioning scheme that all open source packages follow. For example

  • junit 4.10
  • slf4j 1.7.2
  • Hibernate 4.1.7.Final
  • Spring 3.1.2.RELEASE

How does maven figure what is an older vs. newer version of a package in maven? What if package uses letters as versions numbers something along the lines of A,B,C or A2,A2,A4 ... etc.

Is there supposed to be a standard official way to version packages in maven? Are common open source packages like spring and hibernate ignoring this versioning convention?

Gem answered 22/10, 2012 at 2:51 Comment(3)
Don't use version ranges. It was a seductive idea that actually turns out to have been a bad plan. What would be much better would be if Maven allowed directly listing the range and a specified version. The specified version would be what was resolved and the range would be use to provide hints for how far it can be updated and/or what scope of ranges are valid at runtime. Version ranges confuse build-time concerns with run-time concerns, and at present the recommendation with Maven is simple: DON'T USE VERSION RANGESConny
@Stephen I agree I think Maven confuses build time dependency with runtime compatibility, they really need a <comptabileWith> element that's the framework authors list which frameworks they are comptabile with but even that still require a stable way to sort versions numbers. For example spring security 3.1.3 declares a dependency on spring 3.0.7 because it works with both 3.0.7 and 3.1.2 and if you use enforcer plugin to force version convergence then you have to exclude spring as dependency of spring security. I don't use version ranges and I use the enforcer plugin to force convergence.Gem
Note that the accepted answer describes the situation 10 years ago, while Maven 3.0.0 follows the pattern of the answer https://mcmap.net/q/415096/-how-does-maven-sort-version-numbers.Wakeup
D
60

Since version 3.0, Maven uses a consistent system to compare version numbers for both individual versions and version ranges. The system now makes a lot of sense, once you've understood a few gotchas.

All comparisons are now done by ComparableVersion, which says:

  • mixing of '-' (dash) and '.' (dot) separators,
  • transition between characters and digits also constitutes a separator: 1.0alpha1 => [1, 0, alpha, 1]
  • unlimited number of version components,
  • version components in the text can be digits or strings,
  • strings are checked for well-known qualifiers and the qualifier ordering is used for version ordering. Well-known qualifiers (case insensitive) are:
    • alpha or a
    • beta or b
    • milestone or m
    • rc or cr
    • snapshot
    • (the empty string) or ga or final
    • sp
  • Unknown qualifiers are considered after known qualifiers, with lexical order (always case insensitive),
  • a dash usually precedes a qualifier, and is always less important than something preceded with a dot.

This means that versions come out in the following order, which I think makes perfect sense, apart from 1.0-SNAPSHOT right in the middle:

  • 1.0-beta1-SNAPSHOT
  • 1.0-beta1
  • 1.0-beta2-SNAPSHOT
  • 1.0-rc1-SNAPSHOT
  • 1.0-rc1
  • 1.0-SNAPSHOT
  • 1.0
  • 1.0-sp
  • 1.0-whatever
  • 1.0.1

The main gotcha I found in all this is that snapshot comes after beta or rc, so you can't have a development version of 1.0-SNAPSHOT, then release 1.0-beta1 or 1.0-rc1 and have Maven understand that those are later.

Also note that 1.0-beta-1 is exactly the same as 1.0beta1, and 1.0 is exactly the same as 1 or 1.0.0.

Version ranges now work (nearly) the way you'd expect, too. For example, [1.0-alpha-SNAPSHOT,1.0] will find 1.0-beta1-SNAPSHOT, 1.0-beta1, 1.0-rc1-SNAPSHOT, 1.0-rc1, 1.0-SNAPSHOT or 1.0, preferring later items over earlier ones. This is fully supported by mvn versions:resolve, M2Eclipse and so on.

Doriandoric answered 17/7, 2015 at 18:32 Comment(6)
The given reference to Maven Version 3.2.0 is not correct cause this has been changed with Maven Version 3.0 See maven.apache.org/ref/3.0/maven-artifact/apidocs/org/apache/…Vaginitis
You're right. Maven actually uses DefaultArtifactVersion, which used to implement compareTo() by comparing major/minor/incremental/qualifier, falling back to qualifier if it couldn't understand the version. I hadn't spent the time to work out exactly when the implementation of DefaultArtifactVersion was changed so that compareTo() used ComparableVersion, but it appears that it was in version 3.0 as you say: grepcode.com/file/repo1.maven.org/maven2/org.apache.maven/…Doriandoric
I've edited the answer to match. In the Javadoc you referenced, it says that SNAPSHOT comes before 'alpha', but the implementation (on Grepcode) indicates that that's not the case, and is actually after RC as I said in my answer.Doriandoric
Since grep code is down, here is github linkWhorish
@Doriandoric , what do we mean by sp build ? What is full form of it ?Whorish
@Whorish I think it might be "Service Pack", but the meaning is not documented on any Maven page I can find, and I can't find a question in Stack Overflow. There is no "full form" that is understood by Maven - writing servicepack would simply be sorted lexically as an unknown qualifier. Similarly "GA" is apparently "General Availability", but that's not actually documented anywhere.Doriandoric
I
10

This is a test that was written directly against the ComparableVersion class from Maven.

package org.codehaus.mojo.buildhelper.versioning;

import org.apache.maven.artifact.versioning.ComparableVersion;
import org.junit.Assert;
import org.junit.Test;

public class TempTest {
    @Test
    public void testVersions() {
        Assert.assertTrue(new ComparableVersion("1.0-beta1-SNAPSHOT").compareTo(
                new ComparableVersion("1.0-beta1")) < 0);
        Assert.assertTrue(new ComparableVersion("1.0-beta1").compareTo(
                new ComparableVersion("1.0-beta2-SNAPSHOT")) < 0);
        Assert.assertTrue(new ComparableVersion("1.0-beta2-SNAPSHOT").compareTo(
                new ComparableVersion("1.0-rc1-SNAPSHOT")) < 0);
        Assert.assertTrue(new ComparableVersion("1.0-rc1-SNAPSHOT").compareTo(
                new ComparableVersion("1.0-rc1")) < 0);
        Assert.assertTrue(new ComparableVersion("1.0-rc1").compareTo(
                new ComparableVersion("1.0-SNAPSHOT")) < 0);
        Assert.assertTrue(new ComparableVersion("1.0-SNAPSHOT").compareTo(
                new ComparableVersion("1.0")) < 0);
        Assert.assertTrue(new ComparableVersion("1.0").compareTo(
                new ComparableVersion("1")) == 0);
        Assert.assertTrue(new ComparableVersion("1.0").compareTo(
                new ComparableVersion("1.0-sp")) < 0);
        Assert.assertTrue(new ComparableVersion("1.0-sp").compareTo(
                new ComparableVersion("1.0-whatever")) < 0);
        Assert.assertTrue(new ComparableVersion("1.0-whatever").compareTo(
                new ComparableVersion("1.0.1")) < 0);
    }
}

This test asserts that the following versions are considered to be from lowest to highest by Maven:

  • 1.0-beta1-SNAPSHOT
  • 1.0-beta1
  • 1.0-beta2-SNAPSHOT
  • 1.0-rc1-SNAPSHOT
  • 1.0-rc1
  • 1.0-SNAPSHOT
  • 1.0 and 1 (these are equal)
  • 1.0-sp
  • 1.0-whatever
  • 1.0.1
Ist answered 2/11, 2017 at 0:52 Comment(0)
V
2

Your assumption about using major/minor/incremtal/ etc. is simply wrong. The comparision is done in ComparableVersion which contains the implementation. The ctor will call parseVersion(...) which uses ComparableVersion which is stored as instance in DefaultArtifactVersion and it's used during the compareTo(..)

There are parts like getMajor.., etc. but those are not working correctly. This is the reason why will be marked deprecated.

The information by Stehpen Collony is true for Maven 2 but not for Maven 3 anymore.

Vaginitis answered 18/5, 2017 at 11:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.