CMake Dependency Management

Viewed 917

I am looking for some insights in proper CMake dependency management. I've come across ExternalProject and FetchContent and both don't completely fulfill my needs. Let me explain in detail:

My project has the following structure:

main_project/
├── CMakeLists.txt
├── external/
│   ├── submodule1/
│   ├── submodule2/
│   ├── submodule3/
│   .
│   .
│   .

Each submoduleN is a git submodule which contains a full grown up CMake project with its own install targets. I have full control over those submodules (I write them). I want to use install targets because they provide the external interface to the submodules (with proper namespacing etc.). I do not want to use the internal build targets. I want to use find_package to resolve dependencies.

Secondly, I do not want to use some download mechanism to get and deploy depedencies. I want to use git submodules for this, because

  • I can freeze submodule version in the main project (dependency version management).
  • I can checkout any other version easily for debugging (no editing of files, just use git and have the full commit messages for reference at hand).

Assume that each submoduleN has its own, similar substructure and they possibly depend on each other internally. I want them to use a single, common version if they happen to have a shared dependency. Hierarchy in the main project takes precedence for the version selection. For instance, something similar as FetchContent's populate idiom:

FetchContent_GetProperties(mylib)
if(NOT mylib_POPULATED)
  FetchContent_Populate(mylib)
  # do build, install and other stuff
endif()

I want install targets to be usable at the configure step (i.e. find_package) and I want them to be rebuilt if I edit them (i.e. apply some debugging changes in one submoduleN).

Problem:

  • I cannot use ExternalProject_Add because it's only available at build stage.
  • I cannot use FetchContent because it ignores install targets (doesn't even build them).
  • I cannot use FetchContent because there will be duplicate target names (doc, main, tests, etc.).

I know of Hunter++, which almost solves my problem, except for the gitsubmodule and no-download part. I made a partly working example with ExternalProject, but I find it very messy and it doesn't solve the common version problem:
https://github.com/image357/cmake_tutorial_external

Any ideas?


EDIT:

I just found out about a proposal at CMake for something similar:
https://gitlab.kitware.com/cmake/cmake/-/issues/21687

1 Answers

Quick update: It seems that Hunter has a git submodule mode: https://hunter.readthedocs.io/en/latest/user-guides/hunter-user/git-submodule.html

Thus it actually fits my use case perfectly.

Have this is in your CMakeLists.txt

include(your/local/path/to/HunterGate.cmake)
HunterGate(
        URL "https://github.com/cpp-pm/hunter/archive/v0.23.320.tar.gz"
        SHA1 "9b4e732afd22f40482c11ad6342f7d336634226f"
        LOCAL
)

hunter_add_package(mysubmodule_lib)
find_package(mysubmodule_lib CONFIG REQUIRED)

Have this at cmake/Hunter/config.cmake in your project:

hunter_config(mysubmodule_lib GIT_SUBMODULE your/path/to/mysubmodule_lib VERSION 1.2.3)
Related