Since release 3.19 CMake allows to specify a range in find_package() calls:
find_package(Dummy minVersion...maxVersion)
Although currently not much find modules support version ranges one might want to set the upper end to a maximum version the code is compatible with. This can be seen as a safeguard against breaking changes in an unknown future major version.
Let say we have a package “Dummy” whose major versions 1 and 2 are known to work with our own code. One might be tempted to specify the version range like the following:
find_package(Dummy 1...2)
This works well with 1.0.0, 1.5.0 or 2.0.0, but will break with 2.0.1 or 2.1.0.
Instead use the following syntax to include major versions 1 and 2:
find_package(Dummy 1...<3)
But why so?
Let’s have a look at FindPackageHandleStandardArgs.cmake at the function find_package_check_version
which most find modules use:
if ((${package}_FIND_VERSION_RANGE_MIN STREQUAL "INCLUDE"
AND version VERSION_GREATER_EQUAL ${package}_FIND_VERSION_MIN)
AND ((${package}_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE"
AND version VERSION_LESS_EQUAL ${package}_FIND_VERSION_MAX)
OR (${package}_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE"
AND version VERSION_LESS ${package}_FIND_VERSION_MAX)))
The version check uses regular if clauses with the syntax for version tests. Taking a closer look to the documentation reveals that the version check is done component-wise (major.minor.patch) and ommitted components are treated as zero. So writing
find_package(Dummy 1...2)
in the same as
find_package(Dummy 1.0.0...2.0.0)
Now its clear and logical that this will match 2.0.0, but not 2.1.0. See this github repo for an example.
However it still feels a bit unnatural as other version range implementations handle major version ranges differently. node-semver e.g. which is used among the nodejs eco system includes “2.1.0” in the version range “1 – 2”:
const semver = require('semver');
a = semver.satisfies('2.0.0', '1 - 2');
b = semver.satisfies('2.1.0', '1 - 2');
c = semver.satisfies('3.0.0', '1 - 2');
console.log(a); // true
console.log(b); // true
console.log(c); // false