`write_basic_package_version_file` and version range

From reading the templates used by write_basic_package_version_file it seems that the parent project has the ultimate say of how the find_package works, e.g. if SameMajorVersion is used, passing a range like 2.0...4.0 will always fail because only version ranges with the same major are supported.

But then the question is, if the consuming project has a special handling for those ranges so that all of them are supported, how should we design around the packaged project’s restrictions? My only guess is to design a Find<Package>.cmake wrapper where the version range is processed, setting the final <Package>_FOUND variable.

Edit: I’ve noticed a flaw with the Find<Package>.cmake approach. Let’s assume we are consuming via

FetchContent_Declare(ProjectA
		GIT_REPOSITORY https://github.com/user/project-a
		GIT_TAG v2.3.0
		FIND_PACKAGE_ARGS MODULE
		)

With a FindProjectA.cmake file like:

find_package(ProjectA CONFIG)
if (ProjectA_VERSION VERSION_LESS 2)
  set(ProjectA_FOUND FALSE)
elseif (ProjectA_VERISON VERSION_GREATER 3)
  message(WARNING "Support for ProjectA ${ProjectA_VERISON} is experimental. Please report to ...")
endif ()

The issue is that within the FindProjectA.cmake, the targets were already defined by find_package(CONFIG), so when it goes through FetchContent path as the fallback, the target names can potentially clash.

1 Like

I don’t know that the templates ever considered version ranges; I suspect that it is older than range support at all (@brad.king?).

As for conflicting targets, yes, that seems like a problem. However, I think it is more that dependency management needs to be a bit more uniform within a single CMake project rather than “handle all possible conflicts”. @craig.scott?

1 Like

The current implementation of write_basic_package_version_file() pre-dates version ranges. It provides some common version strategies, with SameMajorVersion essentially corresponding to semantic versioning. The use case discussed here is not semantic versioning, so SameMajorVersion isn’t appropriate. But if the package’s project does follow semantic versioning, then consumers must abide by that and can’t simply assume some other versioning logic when calling find_package(). The alternative would be to leave out any version specification from the find_package() call, and implement something like the VALIDATOR support for find_package(). At the moment, VALIDATOR is only supported for find_file(), find_path(), find_library(), and find_program(). CC: @marc.chevrier

A well-behaved config package file will only create targets if the find_package() call will succeed. This recommendation is frequently violated though. If a config package file creates targets but the find_package() call doesn’t deem the package to be found, that’s a bug in the config package file logic, not something that we should try to support workarounds for in CMake.

To be clear, projects should never explicitly set <packageName>_FOUND themselves. Those variables should be set only as part of calls to find_package(). Projects that try to clear or modify such variables are broken, in my view.

That sounds good, but what would be passed to it? Seems like only project version is doable before a target is defined? It would be nice if there was a dry run variant of find_package that would only collect the tatget names (and maybe properties)

The issue is if find_package(CONFIG) is called within a Find<Package>.cmake. In that case, even if the config package is well behaved, you might want to try again with other version constraints, or with pkg-config, etc.

write_basic_package_version_file() is able to generate a version file which is aware of versions range but in the context of the constraints specified (i.e. AnyNewerVersion or SameMajorVersion or |SameMinorVersion).

Now, for a more smart handling of versions range, the best approach is to update the package itself (i.e. Version.cmake file).

Adding a VALIDATOR argument can be an interesting enhancement for specific cases, but a deep evaluation must be done to take care of all possible implications…