Montage Package Dependency Notation

Unified Vs. Side-By-Side Packages

With Montage, a "Target Package" refers to a specific package being installed to a specific Installation Target. Typically, only one version of a package can be installed to an Installation Target, and this version is selected by "unifying" multiple requests for different versions of that package. If a new installation request results in no single package version being able to satisfy all requests, then the requests are over-constrained and the new installation request will fail to unify to the domain.

Not all Installation Targets are the same, however, as some Installation Targets allow multiple versions to be installed "Side-By-Side". As an example, MPLAB X is installed to the Installed Applications Installation Target (where all normal Windows installations are installed), and multiple versions of MPLAB X can be installed simultaneously. Other applications, however, may require that only one version be installed at a time. In this case, in the same Installation Target, some packages are Side-By-Side and some are Unified, depending on the package. In this case, the Installation Target is not designed to force all packages to be installed Side-By-Side, however a package can indicate to Montage whether it should be Unified or can be installed Side-By-Side.

In other Installation Targets, the Installation Target may direct Montage to disable unification regardless of the package's metadata, and install all packages side-by-side. For example, if packages are being installed to a cache, the intent is likely that all versions are cached as they are handled. For that same package, all versions could be installed Side-By-Side into an ecosystem cache and then later a single unified version may be installed into the ecosystem's tool.

The distinction between Side-By-Side and Unified Target Packages is important to understand the mechanics of Montage, however dependency notation is typically given in the context of single package version unification.

Introduction To Dependency Notation

When a package "A" depends on another package "B", the dependency must be specified using Montage Package Dependency Notation. The dependency notation uses a format convention to control what versions of a package are acceptable out of all available versions. After the available versions are filtered, the notation also determines which final version from the acceptable versions is selected for installation.

If Package A depends on exactly version 2.0.0 of Package B, the dependency notation is as follows:

[2.0.0]

The square brackets before and after 2.0.0 indicate an inclusive lower and upper bound, respectively. Since both the lower and upper bounds are inclusive, and the only version between them is 2.0.0, then the only version that will satisfy the dependency will be version 2.0.0. Different lower and upper bounds separated by a comma could also be used, such as this one.

[2.0.0,2.1.0]

This notation indicates that any version greater than or equal to 2.0.0 and less than or equal to 2.1.0 is acceptable. The table below shows the basic interval notation used to specify dependencies.

# Notation Applied Filtering Rule Description
1 1.0 x > = 1.0 Minimum version, inclusive
2 [1.0] x == 1.0 Exact version match
3 (,1.0] x ≤ 1.0 Maximum version, inclusive
4 (,1.0] x < 1.0 Maximum version, exclusive
4 [1.0,2.0] 1.0 ≤ x ≤ 2.0 Exact range, inclusive
5 [1.0,2.0] 1.0 ≤ x ≤ 2.0 Exact range, exclusive
6 [1.0,2.0] 1.0 ≤ x ≤ 2.0 Inclusive min, exclusive max

Semantic Versioning

Montage uses SemVer2.0.0 package versioning, which consists of a Major, Minor, Patch, and optional Pre-Release and Build components. If you are not familiar with Semantic Versioning, you should read the SemVer2.0.0 specification.

https://semver.org/

The examples in the table above are valid dependency notations but didn't reference patch versions. Semantic versions require the patch version to be specified, thus for any notation with an omitted patch, a patch version of 0 is implied.

Pre-Release And Build

The examples in the table above also didn't include Pre-Release or build version components. In Montage version dependency notation, version dependency notations don't reference the Build component. The build component is available in SemVer2.0.0 to provide additional information about the build, however it should not be used to distinguish one version from other versions of the same Major/Minor/Patch/Pre-release version. Thus, for dependency notation purposes, it is completely ignored.

With Montage dependency notation, the Pre-Release versions dependency interval is evaluated completely independently of the normal Major/Minor/Patch dependency interval. If Pre-Release versions are not explicitly referenced in an interval notation, then Pre-Release versions are filtered out and not used. The underlying theoretical reason for this is more fully explained in the section at the bottom titled "Cardinals, Ordinals, And Nominals, Oh My!". This section can be useful for clarifying what can be a somewhat confusing subject once Pre-Release versions are brought into the mix, however they don't normally need to be considered.

When dealing with Pre-Release versions, the notation rules are as follows:

  • The Pre-Release version is treated independently from the main version

  • If a Pre-Release component isn't referenced in a notation, then Pre-Release versions are not accepted by the dependency

  • The same Major/Minor/Patch interval notation rules as above apply for Pre-Release interval notation.

Consider the dependency specified as follows, with the set of available versions also shown:

In this example, versions 2.0.0 and 2.1.0 would meet the criteria, because the upper range limit of 3.0.0 is exclusive, and pre-release versions aren't accepted by the dependency (pre-release versions are assumed to be potentially buggy).

Now consider this dependency:

Version 3.0.0-beta and 3.0.0 are accepted with respect to their main version because the upper limit of 3.0.0 was inclusive. The beta versions are accepted because the prerelease dependency notation of -beta means that prerelease versions are acceptable as long as the prerelease evaluates to greater than or equal to the beta prerelease.

The dependency below specifies non-interval notation for the main version and interval notation for the Pre-Release version:

2.0.0-(alpha,]

With that notation, any main version >= 2.0.0 would be acceptable, and it would be acceptable if it was a Pre-Release version, as long as the Pre-Release version was greater than alpha.

These examples only consider a single dependency requirement, however a package in general will have multiple dependency requirements registered, and all dependency requirements must be satisfied. If no single version meets all requirements, then this either results in a unification error (for unified packages) or it results in two versions of the package being installed (for side-by-side packages).

Version Selection

The descriptions above describe how versions are filtered, but don't explain how a final version is selected when multiple versions meet the filters. Montage will, by default, select based on the following default preferences:

  • Lower Major versions are preferred over higher Major versions. As per the Semantic Versioning convention, higher Major versions are expected to have breaking changes, and so are only accepted as a last resort. A common practice is to use the next Major version as a non-inclusive upper bound, so that all versions up to the next new Major version are accepted. Example: a dependency of (,3) will accept any version up to but excluding version 3.0.0. Thus, newer versions of 2.X would be accepted as they are made available.

  • Lower Minor versions are also preferred over higher Minor versions. Although Minor versions are expected to introduce new functionality without producing breaking changes, there is more chance that a new Minor version has bugs.

  • Lower patch versions are preferred over higher patch versions. Although it is assumed that higher patch versions have less issues than lower patch versions, if the lowest version has been tested and is known to work, it is by default the preferred. This can be easily changed using wildcards.

  • Lower Pre-Release versions are preferred over higher Pre-Release versions. Since higher Pre-Release versions (i.e. Beta) are expected to be more stable than lower Pre-Release versions (i.e. Alpha), it is generally recommended to override this default behavior using a wildcard.

Wildcards

Wildcards can be used in the Major version, or Minor version, or Patch version, or at the end of the Pre-Release version, to indicate that any version that matches all other constraints are acceptable. Further, wildcards indicate that the highest version should be selected, as opposed to the default lowest version. For the dependency 4.1.*, any main version with a Major version of 4 and Minor version of 1 would be acceptable, and the version with the maximum Patch version would be selected.

A dependency of 4.* would result in the highest version of Major version 4 being selected. Note that the Patch is not referenced when the Minor version is wildcard, thus 4.*.* is an invalid dependency notation. The specification of a wildcard in the Major version is not generally recommended, as Major versions introduce breaking changes. However, since versions of installed applications commonly increment the Major version, the wildcard notation for the Major version is accepted by Montage.

Cardinals, Ordinals, and Nominals, Oh My!

Package versions, version dependencies and in particular version unification results can be difficult to conceptualize. Some basic theoretical background regarding what Semantic 2.0 Versions are can be useful.

A "Nominal" is a number that is used to simply uniquely identify something, such as the number on a player's jersey. A "Cardinal" is a number that represents a count of something, without any regard to the relationships between the items being counted. And an "Ordinal" is a number given to indicate an order of an item in a list, i.e. an indication of an item's relationship with respect to other items.

Semantic Versioning assigns semantic meaning to elements of a versioned article, making it easier to identify the best version of the article to be used in a use context. The versions behave similar to all three types of numbers: cardinals, ordinals and nominals. They provide a nominal to uniquely identify a release, they can be used to count the number of releases, and they provide ordinal information about earlier and later releases.

The use of Minor and Patch versions adds some ambiguity regarding which versions are "earlier" and "later" releases, since version 2.3.4 of an article could be released after version 3.1.0 of the same package, if the intent is to fix a bug to an earlier version that is still otherwise valid. With the simple addition of Minor and Patch notation, the waters of ordinality are already muddied. Given a set of acceptable versions that span across multiple Minor and Patch versions, for Unified Target Packages the set is reduced to a single version. If the notation prescribes the "greatest" version, then the Major version is compared first, with lower Major version discarded. Then Minor versions are compared, then Patch, until a single version remains. For Minor versions, the same reduction process is used, starting with the Major version, but taking the lower number.

Aside from this, the main versions (Major/Minor/Build) in a set can be thought of as ordinal along a single axis. Pre-Release versions are a bit of an anomaly, as it is not a given that 3.2.0-beta is "greater" or "better" than 3.1.32. The set of pre-release versions of a specific main version should be thought of as an independent ordinal set of versions, not clearly related to the ordinal set of release versions. In Montage dependency notation, when pre-release versions aren't specified, they are excluded by default. When they are specified, they define an interval that is evaluated independently from the main version interval. The Pre-Release interval notation, if indicated, can be thought of as the vertical bounds, and the main version interval notation as the horizontal bounds of a box when the versions are organized as shown below.

Copyright © 2021 Montage Software, LLC – All Rights Reserved