How does maven sort version numbers?

2019-01-10 19:36发布

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?

标签: maven maven-3
4条回答
女痞
2楼-- · 2019-01-10 20:05

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
查看更多
闹够了就滚
3楼-- · 2019-01-10 20:11

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.

查看更多
叼着烟拽天下
4楼-- · 2019-01-10 20:18

According to this reference, Maven requires that version be of the form:

<MajorVersion [> . <MinorVersion [> . <IncrementalVersion ] ] [> - <BuildNumber | Qualifier ]>

Where MajorVersion, MinorVersion, IncrementalVersion and BuildNumber are all numeric and Qualifier is a string. If your version number does not match this format, then the entire version number is treated as being the Qualifier.

The reference goes onto explain that you may "face issues" (particularly with Maven version ranges) if you work with artifacts that do not follow this versioning scheme. Looking at the Maven source code that is linked from the article is quite helpful.

Of the examples you've given, the Hibernate and Spring artifacts seem to deviate in their use of "." rather than "-" to separate the qualifier.

Some experimentation on my part shows that DefaultArtifactVersion will parse the version numbers exactly as described above. That is, the Spring example given (3.1.2.RELEASE) will be interpreted as:

  • Major: 0
  • Minor: 0
  • Incremental: 0
  • Qualifier: 3.1.2.RELEASE

More importantly the comparison of two version numbers (if using Maven 3.0 or newer) is much more flexible. The version numbers are split into lists of items (where either . or - marks the boundary between items, note that . has a higher priority than -). Comparison is then accomplished by taking each item in term and performing a natural order comparison. Hence 3.1.2.RELEASE will be seen as smaller than 3.1.3.RELEASE. Strings are compared too, hence 3.1.2.RELEASD will be seen as smaller than 3.1.2.RELEASE. There are some special strings that have special equivalents, for example 3.1.3.a.1 will sort the same as 3.1.3.alpha.1

However, while the comparison is more flexible in newer versions of Maven. The Version Range Boundaries are still evaluated using the Major:Minor:Incremental:Qualifier scheme, so if you are using version ranges the flexibility is less useful.

查看更多
别忘想泡老子
5楼-- · 2019-01-10 20:22

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.

查看更多
登录 后发表回答