Thoughts on using CPM.cmake as FetchContent Alternative

It seems like for the short term, we are using FetchContent to get testing dependencies into Beman libraries. I personally think this is a fine solution for simple test dependencies but I wanted to throw CPM.cmake out there as it is something we use quite a bit and it works quite well for us in our use case at Xstrahl.

One thing I like about it is that you can adapt it’s behavior to use only local packages with CMake flags.

A simple way to include it is found here. It would add another download for building with tests enabled, but this would only apply to devs working on a project. Consumers would not be affected by this.

Please note that every CMake add_subdirectory-based solution does not scale. This includes both FetchContent and CPM.

If we start using our libraries as dependencies on our other libraries (again, probably with FetchContent or CPM), we may have some problems because of that. The most severe typical issues are:

  • CMake target duplicates that fail CMake builds,
  • CTest tests of dependencies in our CTest runs,
  • GTest version conflicts,
  • long builds and runs (all dependencies will also fetch their testing frameworks and try to run them).

I agree, this is not a way to handle large number of dependencies. But for simple things like GTest or doctest it works well enough and is quite simple.

If we plan on adding dependencies between different beman libraries, it would probably be better to set up a custom package index if using vcpkg or use Conan.

There are Cmake idioms for not building and running tests if you are not the top level project, so we should be able to avoid the repeated testing exponential problems.

It would be very infeasible if we were moving outside the standard library, but since most of it is not compiled into libraries we should also be able to avoid building more than we need for testing the components under test.

But, without some sort of package management system that generates consistent artifacts for a link context, we’re not going to scale much beyond the standard library.

1 Like

From Professional CMake 19th edition:

# CMake 3.21 or later is required for PROJECT_IS_TOP_LEVEL
option(MYPROJ_ENABLE_TESTING "..." ${PROJECT_IS_TOP_LEVEL})
if(MYPROJ_ENABLE_TESTING)
  add_subdirectory(tests)
endif()
if(PROJECT_IS_TOP_LEVEL)
  add_subdirectory(packaging)
else()
  # Users are unlikely to be interested in testing this
  # project, so don't show it in the basic options
  mark_as_advanced(MYPROJ_ENABLE_TESTING)
endif()

This is the recommended idiom for a project meant to be consumed as a cmake project by other cmake projects.

Note - packaging is mentioned here, but we may end up doing it all somewhat differently; the packaging subdir is if you’re using the CMake packaging tools.

1 Like

Thanks for the video link. That was interesting.

My understanding is that this is mitigated by our naming convention which has all target names prefixed with beman.<short_name>.

I think this is mitigated with the technique @Sdowney mentioned above or passing BUILD_TESTING=OFF when invoking FetchContent_Declare which is what we do in beman.exemplar.

I think this is mitigated by FetchContent_Declare since the first call takes precidence over all subsquent calls.

I think this is also mitigated by the BUILD_TESTING=OFF FetchContent_Declare technique.

Can you think of any other issues that we need to think about @mpusz?

I assume you’re talking about local package override. Yeah, I can see that coming in really handy for large projects.

I wonder how you would do that with FetchContent_Declare. I guess you’d have to put a FetchContent_Declare call pointing to the local repository in the top-level CMakeLists.txt file of your project.

For reference, CPMAddPackage is used 3.6k times in GitHub and FetchContent_Declare is used 35.7k times.

I actually meant the USE_LOCAL_PACKAGES flag. You can force CPM to work only with find_package.

CPM seems to create a cache of packages that are added to it and it can decide what to do for dependencies based on if the above environment flag is set or not. If it is it just delegates to find_package and otherwise it uses the regular FetchContent machinery.

For the record, we started using FetchContent because we started tooling in a hackathon and it was trivial to bootstrap.

To me, this discussion thread is evidence that we’re ready to revisit the use of FetchContent and move to proper packaging. I believe effort sunk into developing styles and conventions to support recursive vendoring workflows are better spent getting Conan and/or vcpkg working for us.

I do think we should shoot for FerchContent friendly projects, but there’s a general portability problem when every project needs to detect whether it’s in some environment or another before it decides to perform this or that operation. That’s a pear-shaped approach to an ecosystem consistency problem. We would be expecting every project to be manually maintained to be consistent with every other project. And all projects to get individual updates as our expectations evolve.

1 Like

It looks like FetchContent_Declare() has a similar variable, FETCHCONTENT_TRY_FIND_PACKAGE_MODE. I wonder if the CMake folks are porting the popular CPM features into fetch content directly.

1 Like