Unified way of describing dependencies of a project
For context, this has been split out of #22619 (closed) and could be considered to supersede #21687 (closed).
The main goal of this proposal is to unify find_package()
, FetchContent
and FindPkgConfig
such that there is a single way of describing the dependencies of a project. This immediately gives the following consequences:
- Need to support both pre-built binaries and building from source (using the toolchain of the project).
- Would need to support not just a version number, but also things like git commit hashes and pkg-config module specs (i.e. basically the union of what
find_package()
,FetchContent
andpkg_check_modules()
can handle).
Ideally, the way the dependencies are specified should be both human-readable and machine-readable. That would likely mean using something like a "lock file", which is a common approach in some languages.
- File format should be easy for people not from a computer science or software engineering background to understand and use.
- File format would ideally be familiar and an existing format which already has good support in IDEs, etc.
- JSON seems the obvious leading candidate (EDIT: don't focus on this, the format isn't the most important part of this proposal).
- If JSON is used, the schema should allow a
comment
field in most objects. The official JSON format doesn't support comments, but being able to provide them would be desirable for human readability use cases. Thecomment
field would never affect processing, it would only be for human information and recording arbitrary text. - Should be agnostic to the build system used, not CMake-specific, but should probably allow build-system-specific details to be provided (like how "vendor" objects work in CMake presets, for example).
- Needs to have a well-defined file name and would be found at the top of the source tree of a project.
- Need clear semantics about how lock files nested down in lower directories of a project would be considered. Specifically, if a dependency is brought in via
add_subdirectory()
, should its lock file be read as well? If so, presumably any details already set by the parent should override any specified by dependencies (as a guide, this approach has worked pretty well forFetchContent
).
In order to support different use cases, the lock file would need to be able to specify a dependency as "required", "optional" or "on demand". These would have the following semantics:
- "Required" means the project always needs it and the dependency can be populated up front. This is what existing package managers essentially need and implement today.
- The "optional" case is like saying "try to populate this up front, but it isn't an error if it fails".
- The "on demand" case is like saying "defer actually trying to populate this dependency until we know something actually needs it". Failing to provide it when asked to do so would be considered a fatal error.
Both "optional" and "on demand" assist with situations where a project decides what features to enable based on whether particular dependencies are available. "Optional" would populate up front and could populate more dependencies than needed, but fits fairly well with existing package manager approaches (they could treat them similar to "required" but not make failure an error). "On demand" has the potential to be more efficient, but could still technically be implemented as "required" if the dependency provider isn't able to offer lazy population. Note that a lock file is static and doesn't consider that CMake cache variables and options may turn some parts of the build on or off. This "on demand" mechanism provides a way for the lock file to handle this scenario more efficiently, where the dependency provider fully implements it.
The "on demand" case would require providing some kind of hook or event handler that dependency providers could register with and intercept calls to find_package()
, FetchContent_MakeAvailable()
, pkg_check_modules()
, etc. Ideally, there would be a single mechanism to register for rather than having to register for different kinds of requests.
It is an open question how to handle platform-specific dependencies. Ideally, we wouldn't want to have to standardise target platform names, since there are arbitrary possibilities, including private ones that have confidentiality requirements. Such cases might be handled already by "on demand" dependencies.
So that there is no confusion, this proposal is in some respects a complement to CPS (Common Package Specification), but is not in any way reliant on it or directly associated with it. CPS is associated with what an installed package provides, this issue here is associated with defining what is needed when building a project. There may, however, be some themes and considerations that are common to both. #20106 is also potentially at least partially relevant (but it is focused on CPS).