1

I want my repo to depend on a package with a certain allowed version range. So, looking at the documentation, I write:

find_package(mypkg 0.3.3...<0.5.0 REQUIRED)

but when I configure with CMake (v3.23.0-rc2), this line yields:

CMake Error at CMakeLists.txt:24 (find_package):
  Could not find a configuration file for package "mypkg" that is
  compatible with requested version range "0.3.3...<0.5.0".

  The following configuration files were considered but not accepted:

    /opt/mypkg/lib64/cmake/mypkg/mypkg-config.cmake, version: 0.4.6

Why is 0.4.6 not compatible with 0.3.3...<0.5.0 ? Or - is something else wrong?

Additional information:

  • The using repo's CMakeLists.txt has:

    cmake_minimum_required(VERSION 3.18 FATAL_ERROR)
    

    ... changing it to 3.19 doesn't help.

  • By popular request, here's the package's auto-generated version config file:

# This is a basic version file for the Config-mode of find_package().
# It is used by write_basic_package_version_file() as input file for configure_file()
# to create a version-file which can be installed along a config.cmake file.
#
# The created file sets PACKAGE_VERSION_EXACT if the current version string and
# the requested version string are exactly the same and it sets
# PACKAGE_VERSION_COMPATIBLE if the current version is >= requested version,
# but only if the requested major and minor versions are the same as the current
# one.
# The variable CVF_VERSION must be set before calling configure_file().


set(PACKAGE_VERSION "0.4.6")

if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION)
  set(PACKAGE_VERSION_COMPATIBLE FALSE)
else()

  if("0.4.6" MATCHES "^([0-9]+)\\.([0-9]+)")
    set(CVF_VERSION_MAJOR "${CMAKE_MATCH_1}")
    set(CVF_VERSION_MINOR "${CMAKE_MATCH_2}")

    if(NOT CVF_VERSION_MAJOR VERSION_EQUAL 0)
      string(REGEX REPLACE "^0+" "" CVF_VERSION_MAJOR "${CVF_VERSION_MAJOR}")
    endif()
    if(NOT CVF_VERSION_MINOR VERSION_EQUAL 0)
      string(REGEX REPLACE "^0+" "" CVF_VERSION_MINOR "${CVF_VERSION_MINOR}")
    endif()
  else()
    set(CVF_VERSION_MAJOR "0.4.6")
    set(CVF_VERSION_MINOR "")
  endif()

  if(PACKAGE_FIND_VERSION_RANGE)
    # both endpoints of the range must have the expected major and minor versions
    math (EXPR CVF_VERSION_MINOR_NEXT "${CVF_VERSION_MINOR} + 1")
    if (NOT (PACKAGE_FIND_VERSION_MIN_MAJOR STREQUAL CVF_VERSION_MAJOR
          AND PACKAGE_FIND_VERSION_MIN_MINOR STREQUAL CVF_VERSION_MINOR)
        OR ((PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE"
            AND NOT (PACKAGE_FIND_VERSION_MAX_MAJOR STREQUAL CVF_VERSION_MAJOR
              AND PACKAGE_FIND_VERSION_MAX_MINOR STREQUAL CVF_VERSION_MINOR))
          OR (PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE"
            AND NOT PACKAGE_FIND_VERSION_MAX VERSION_LESS_EQUAL ${CVF_VERSION_MAJOR}.${CVF_VERSION_MINOR_NEXT})))
      set(PACKAGE_VERSION_COMPATIBLE FALSE)
    elseif(PACKAGE_FIND_VERSION_MIN_MAJOR STREQUAL CVF_VERSION_MAJOR
        AND PACKAGE_FIND_VERSION_MIN_MINOR STREQUAL CVF_VERSION_MINOR
        AND ((PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" AND PACKAGE_VERSION VERSION_LESS_EQUAL PACKAGE_FIND_VERSION_MAX)
        OR (PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" AND PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION_MAX)))
      set(PACKAGE_VERSION_COMPATIBLE TRUE)
    else()
      set(PACKAGE_VERSION_COMPATIBLE FALSE)
    endif()
  else()
    if(NOT PACKAGE_FIND_VERSION_MAJOR VERSION_EQUAL 0)
      string(REGEX REPLACE "^0+" "" PACKAGE_FIND_VERSION_MAJOR "${PACKAGE_FIND_VERSION_MAJOR}")
    endif()
    if(NOT PACKAGE_FIND_VERSION_MINOR VERSION_EQUAL 0)
      string(REGEX REPLACE "^0+" "" PACKAGE_FIND_VERSION_MINOR "${PACKAGE_FIND_VERSION_MINOR}")
    endif()

    if((PACKAGE_FIND_VERSION_MAJOR STREQUAL CVF_VERSION_MAJOR) AND
        (PACKAGE_FIND_VERSION_MINOR STREQUAL CVF_VERSION_MINOR))
      set(PACKAGE_VERSION_COMPATIBLE TRUE)
    else()
      set(PACKAGE_VERSION_COMPATIBLE FALSE)
    endif()

    if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION)
      set(PACKAGE_VERSION_EXACT TRUE)
    endif()
  endif()
endif()


# if the installed project requested no architecture check, don't perform the check
if("FALSE")
  return()
endif()

# if the installed or the using project don't have CMAKE_SIZEOF_VOID_P set, ignore it:
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "" OR "8" STREQUAL "")
  return()
endif()

# check that the installed version has the same 32/64bit-ness as the one which is currently searching:
if(NOT CMAKE_SIZEOF_VOID_P STREQUAL "8")
  math(EXPR installedBits "8 * 8")
  set(PACKAGE_VERSION "${PACKAGE_VERSION} (${installedBits}bit)")
  set(PACKAGE_VERSION_UNSUITABLE TRUE)
endif()
einpoklum
  • 118,144
  • 57
  • 340
  • 684

1 Answers1

1

In short: The version range 0.3.3...<0.5.0 is incorrect from the view of the compatibility policy, specified for the package. Given package is treated as compatible only when both major and minor versions in the request are the same as the package's version.

Correct version requests could be:

  • 0.3.3...<0.4.0
  • 0.4.3...<0.5.0

(But the package with version 0.4.6 would satisfy only the second request).

Details

The config file is generated by write_basic_package_version_file with the COMPATIBILITY option SameMinorVersion. This can be deduced from the description:

# ... it sets
# PACKAGE_VERSION_COMPATIBLE if the current version is >= requested version,
# but only if the requested major and minor versions are the same as the current
# one.

If requested a version range with both ends included (without <), then both ends should have same major and minor versions:

# both endpoints of the range must have the expected major and minor versions

When upper end is excluded (with <), then it could have minor version greater by 1 than the lower end.

Allowed:

  • 3.3.0...3.3.5
  • 3.4.2...3.4.10
  • 3.3.0...<3.3.5
  • 3.3.2...<3.4

Disallowed:

  • 3.3.0...3.4.0
  • 3.3.0...4.3.0
  • 3.3.0...<3.5
  • 3.3.0...<3.4.1

Only when version range is allowed, the package's version is checked to be inside the range. In other cases the package is treated as incompatible with the request.


From my understanding, the version request semantic in find_package call is:

  • In case of single version requested, the package version should be compatible with requested version. And the package version should be no less than the requested one.
  • In case of version range without <, the package's version should be compatible with both ends. And the package version should belong to the range.
  • In case of version range with <, the upper end could denote the "next incompatible" version of the package. In other aspects this case is similar to one without <.
Tsyvarev
  • 60,011
  • 17
  • 110
  • 153
  • I understand what you're saying, but it doesn't make sense. I can write a package whose code supports different versions of the same library, despite them not being compatible with each other. The simplest way is taking preprocessor macros with the library version and conditionally compiling code based on the macro values. Only when I say "I need a version compatible with X.Y.Z) should the package compatibility logic apply. – einpoklum Mar 09 '22 at 19:31
  • "it doesn't make sense" - Well, it has a sense actually. But this sense **differs** from yours. Also, except of the very last part, my answer just describes how it is **currently implemented** in CMake (according to the generated code). If you think that implemented semantic is not convenient (for you and for others), then you could fill an issue to CMake tracker, so its developers will be aware of your point. – Tsyvarev Mar 09 '22 at 19:39
  • 1
    When the **single value** is given for version parameter of `find_package`, it is treated as **compatibility** request. This behavior has been implemented and described in very old CMake versions. I am pretty sure they won't change that behavior. Argument with the version range uses the same "place" as a single value one, so it is logical that this range has "compatibility" aspects too. You may ask them to introduce **new argument** with "pure" version ranges. As for now, you could call `find_package()` **twice**, once with the range `0.4.0...<0.5.0`, and once with the range `0.3.3...<0.4.0`. – Tsyvarev Mar 09 '22 at 20:06
  • @einpoklum the version file is generated for the user convenience but you can change its code to whatever check you see fit. – ixSci Mar 10 '22 at 05:32
  • @ixSci: It's not the file that's the problem. That is, the file doesn't decide what my repo can accept, the file can tell us which versions the installed version is compatible with. But I I see what you're saying. – einpoklum Mar 10 '22 at 07:03
  • @einpoklum maybe I'm mistaken but AFAIK it is only the file that matters, actually. Try to remove its whole content and replace it with: `set(PACKAGE_VERSION_COMPATIBLE TRUE)`. Maybe it would require other _output_ variables to be set also (like `PACKAGE_VERSION`), I'm not sure. But that one is required for sure. So you should be able to tweak the version file to get the behavior you need (which is the correct one in my opinion). – ixSci Mar 10 '22 at 07:14
  • @ixSci: The concern of einpoklum is that he want to use **3d-party package** but apply his **own rules for version checking**, when version compatibility is not needed. So, changing the version file of that package is not an option: this file belongs to the package, not to einpoklum. Also, checking compatibility in the version file follows the **CMake policy**, so changing that file would break other usages of the same package. – Tsyvarev Mar 10 '22 at 07:58
  • If it is for the package you can't control then yes, you are out of luck. But it is worth filing a bug report because this behavior is a bug irrespective of the design decisions the author of the feature made implementing it. The code in the question should work because it is natural. And if you control the package you can change the version file **and** file a bug report to the CMake team. Frankly, that should be easily fixable, just need to get Kitware guys on board. – ixSci Mar 10 '22 at 08:36
  • @ixSci: You're right, but I see this situation as a semantic failing of CMake. It's just like Tsyarev says. I'll file a bug report. – einpoklum Mar 10 '22 at 09:36
  • So, I finally [filed it](https://gitlab.kitware.com/cmake/cmake/-/issues/24581). – einpoklum Mar 08 '23 at 11:46