Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Equinox/p2/Omni Version

< Equinox‎ | p2
Revision as of 14:51, 16 December 2008 by Henrik.lindberg.puppet.com (Talk | contribs) (OmniVersion)

W.I.P - Being Reworked after discussion Dec 15, 2008.

Introduction

This page describes a proposal for adding support for non OSGi version and version ranges in Equinox p2. This page was created as a result of the discussion on the p2 call on Dec 1, 2008. See bug 233699 for discussion.

Background

There are other versioning schemes in wide use that are not compatible with OSGi version and version ranges. The problem is both syntactic and semantic.

Example of semantic issue

Many open source projects do their versioning in a fashion similar to OSGi but with one very significant difference. For two versions that are otherwise equal, a lack of qualifier signifies a higher version then when a qualifier is present. I.e.

1.0.0.alpha 
1.0.0.beta
1.0.0.rc1
1.0.0

The 1.0.0 is the final release. The qualifier happens to be in alphabetical order here but that's not always true.

Example of syntax issue

Here are some examples of versions used in Red Had Fedora distributions.

KDE Admin version 7:4.0.3-3.fc9
Compat libstdc version 33-3.2.3-63
Automake 1.4p6-15.fc7

These are not syntactically compatible with OSGi versions as they use colon, and dash as leading separators.

Some versioning schemes place the qualifier first.

Current implementation in p2

The current implementation in p2 uses the classes Version and VersionRange to describe the two concepts and these are implementations handling only OSGi version type.

Proposed Solution

Equinox p2 should have one implementation of version capable of capturing the semantics of various version formats. The notion of a "version type" is thus deprecated. The advantage over previous proposal is that there is only one implementation of Version (lets call it OmniVersion) so the need to dynamically plugin new implementations goes away, and new formats can be introduced more easily.

Even if the finished solution only requires a single implementation (the OmniVersion), there are other factors to consider. The current p2 SimplePlanner uses the OSGi planner, and it can only understand OSGi versions. There is work being done on SAT4J to enable it being used instead of the OSGi planner (work to handle "explanations" could also be used to handle "attachments" (now being done with OSGi planner). To be able to develop the OmniVersion solution, it needs to coexist with the current SimplePlanner, and thus the current direct use of the classes Version and VersionRange needes to be modified to use IVersion and IVersionType. (Such an implementation is available in bug 233699 as a patch).

  • The interfaces IVersion and IVersionRange should be used throughout the code instead of directly using the corresponding Version and VersionRange classes.
  • An IVersion is obtained by calling a factory method such as VersionFactory.create(String versionString)
  • An IVersionRange is obtained by a similar factory method
  • The version string and version range has a URI scheme like prefix to indicate the version type
  • The factory API can naturally contain some options where scheme and version strings are either separate or canonical
  • When a version or version range is present without the version type prefix, the default is to use OSGi version type (this preserves backwards compatibility).

It is now possible to replace the SimplePlanner's use of OSGi planner with a similar planner that handles OmniVersion. This can be done by someone that needs support for different versions formats before the SAT4j solution is available.

OmniVersion

The OmniVersion implementation uses a segment Array of Object that is kept in order of significance. The Array can hold Integer, String, Date, and the special marker classes MaxString, and MaxDate.

The OmniVersion implements Comparable. Segments are compared with the following rules:

  • if types are not equal the result is based on the significance of the type; Integer > MaxString > String > MaxDate > Date
  • if the type is equal - they are compared and result is returned
  • If all segments are equal up to end of the shortest array, the shorter version is considered smaller

In order to be able to present the version in human readable form, the original version string is also kept. An OmniVersion can be instantiated from a "version string" as described in the Version Format section below.

Version Formats

There are two basic formats - when no format has been specified the version string is an OSGi version string (this ensure backwards compatibility, or raw which fully specifies the format. A ':' separates the format from the version text.

format string description
The default OSGi version type i.e. "1.0.0"
raw(<pattern>) Specifies a raw version type with a transformation pattern. The pattern can contain:
  • characters
  •  %s - an alpha group
  •  %d - a numeric (integer) group
  •  %S - same as %s, but missing input > any input
  •  %D - same as %d, but missing input > any input
  •  %t{
  •  %T{<time format>} - same as %t but missing input > any input
  • [...] - indicates an optional part
  • '*' after [...] means that the optional part can be repeated
  • {<segment significance>} - optional, when placed first in the string it defines the significance of segments - the default is {0,1,2,...}. A significance of -1 means that the segment is not used in the comparison. If used there must be as many entries as there are segments, and all entries must be unique except if the entry is -1.
  • \ - escape must be used to escape ')' '%', and '{', and '*' to include these characters as delimiters, and '\\' is needed to include a '\'

Named Version Formats

Named version formats makes it easier to enter version strings. There should be a number of predefined names as shown in the table below.

type name pattern comment
osgi  %d[.%d[.%d[.%s]]] The default osgi type e.g. "raw(%d[.%d[.%d[.%s]]]):1.0.0", "osgi:1.0.0" and "1.0.0" are all equivalent.
triplet  %d[.%d[.%d[.%S]]] A variation on OSGi, with the same syntax, but where the a lack of qualifier > any qualifier.
maven.snapshot  %d[.%d[.%d[-%D.%T{yyyyMMdd'.'HHmmss}]]] Format used when maven transforms versions like 1.2.3-SNAPSHOT into 1.2.3-<buildnumber>.<timestamp>

The version range delimiters are: '(', ')', '[', ']' and , ',' (comma).

Examples using 'raw'

A raw pattern equivalent to OSGi is written as:

raw(%d[.%d[.%d[.%s]]])

A raw pattern equivalent to triplet is written as:

raw(%d[.%d[.%d[.%S]]])

If the first parsed segment should be the least significant (significance=3) in the comparison, and 'empty > any'

raw({3,0,1,2}[%S:]%d[.%d[.%d]]]):alpha:1.0.0

A version range with format equivalent to OSGi

raw(%d[.%d[.%d[.%s]]]):[1.0.0.r12345, 2.0.0]

At least one string, and max 5 strings

raw(%s[.%s[.%s[.%s[.%s]]]]):vivaldi.opus.spring.bar5

Max depth 8 of numerical segments (classic SCCS type versions):

raw(%d[.%d[.%d[.%d[.%d[.%d[.%d[.%d]]]]]]]):1.1.1.1.1.1.1.4

Numeric to optional depth 8, followed by optional alpha where 'emtpy > any'

raw(%d[.%d[.%d[.%d[.%d[.%d[.%d[.%d]]]]]]][:%S]):1.1.1.4:beta

Uninterpreted string range

raw(%s):[titanic,andrea doria]

Examples using "any"

A required capability is wanted with the range:

any:[1.2,2]

and the following candidates are found:

triplet:1.3.0.alpha
osgi:1.3.0

Both candidates match since range values 1.2 and 2 can be transformed to both triplet and osgi, An ordering of triplet:1.3.0.alpha and osgi:1.3.0 results in 1.3.0.alpha as the greater version.

A required capability is wanted with the range:

any:[1.2,2]

and the following candidates are found:

triplet:1.3.0
osgi:1.3.0.alpha

Both candidates match since range values 1.2 and 2 can be transformed to both triplet and osgi, An ordering of triplet:1.3 and 1.3 results in 1.3.alpha as the latest version since the triplet version is 1.3.0.MAXINT in canonical form

A require capability is wanted with the range:

any:[andrea.doria, titanic]

and the following candidates are found:

osgi:1.2
triplet:1.0
raw(%s):wasa
raw(%s):tirpitz
raw(%s[.d]):mango.3

The range values can not be converted to osgi or triplet. The raw(%s):wasa > "titanic", but "raw(%s):tirpitz is in range. The andrea.doria can not be converted to the format specified in raw(%d[.%d]):mango.3 and since one of the bounds can not be compared, the comparison fails. The version raw(%s):tirpitz is selected.

Example using 'any' as the version type for IU version

It is expected that IUs are published with a known type (i.e. not "any") as the publisher of an IU should have the knowledge to select the appropriate version. In the unlikely case that the publisher is not capable of correctly describing the version syntax/semantics, it is still be possible for the publisher to specify "any". To specify "any" for the IU would be saying "you can interpret this version string any way you like".

If a candidate IU is specified to have the version type "any": - if a query is made using "any" the comparison would be a string comparison of the entire version string (i.e. single segment) - if a query is made using a version type other than "any", the comparison would be made based on if the "any-typed" version string can be transformed to the explicit (or implicit pattern) defined by the "non-any-type". (I.e. a query for 'osgi:1.2.0' would match 'any:1.2.0').

(The ability to specify "any" in a provided capability/IU is under discussion. Use of "any" should perhaps be restricted to be used for required capability only).

Internationalization

The proposed types using alphanumerical segments are assumed to use vanilla string comparison. This does not work so well if versions are expressed in a language where lexical ordering is different. Language specific collation could be supported by combining version type name with the name of a ISO 639 Language code (see java.util.Locale) and where the default would be English. The language could be encoded with a separating '-' e.g. 'raw-pt' for collation in Portuguese.

This opens up another can of worms (decomposition strength, comparison of locale and non locale specified types, etc.), and it is probably best to implement just basic string comparison. It is also questionable if internationalization is wanted at all, as "known tools" does not support this, and "correct collation" would thus yield a different result.

Support for internationalized collation is not recommended.

Version Range

Version range uses the osgi range syntax, but prefixed with version type.

Examples:

  • [1.0.0,2.0.0] equal to osgi:[1.0.0,2.0.0]
  • raw(%s):[titanic,andrea doria]
  • raw(%d:%d[.%d]*[-%dfc%d]:[7:4.0.3-3.fc9,8:1] - an example KDE Admin version 7:4.0.3-3.fc9 to 8:1
  • triplet:[1.0.0.RC1,1.0.0]

Factory API

The factory API could be something simple like this:

public class VersionFactory
{
    IVersion createVersion(String versionString);
    IVersion createVersion(String versionType, String versionString);
}
public class VersionRangeFactory
{
    IVersionRange createVersionRange(String versionRangeString);
    IVersionRange createVersionRange(String versionType, String versionString);
}

Hard to say how much indirection is required - methods could just be static to keep things simple.

If we want to support the pattern based type, the factory methods needs the pattern as well. To make this generic, it could be seen as a paramter to the version type.

public class VersionFactory
{
    IVersion createVersion(String versionString);
    IVersion createVersion(String versionType, String versionString);
    IVersion createVersion(String versionType, String versionTypeParameter, String versionString);
}
public class VersionRangeFactory
{
    IVersionRange createVersionRange(String versionRangeString);
    IVersionRange createVersionRange(String versionType, String versionString);
    IVersionRange createVersionRange(String versionType, String versionTypeParameter, String versionString);
}

When creating a pattern based version, the versionTypeParameter must be supplied. When creating a pattern based version range, the pattern is optional - the pattern of the individual candidates would then be used to create the canonical form of the upper and lower bounds.

IVersion and IVersionRange API

Basically follows the current Version and VersionRange classes.

Applicability

The generalization of version type applies to objects that by nature may have a different versioning scheme than OSGi. This includes:

  • Installable Unit
  • Provided Capability
  • Required Capability

This needs to be discussed:

  • Artifact key versions

These does (probably) not need to be generalized:

  • File format version numbers (content.xml, artifact.xml, etc)
  • Update Descriptor
  • Touchpoint version numbers and touchpoint action versions
  • Publisher advice versions
  • Artifact key versions

Implementation Steps

  1. Enablement
    1. Change use of Version and VersionRange to use IVersion and IVersionRange
    2. Implement the VersionFactory and VersionRangeFactory by just creating instances of Version and VersionRange.
    3. Make sure UI validates human input based on factory result
  2. Basic Extensions
    1. Implement the non pattern based version types
    2. Modify the factories to create correct instances of the types
  3. Pattern based implementation
    1. Implement the pattern based version types (raw and any)
    2. Modify the factory
    3. Modify the comparator(s)
    4. Adjustments to UI ? (should not be needed)

Back to the top