Is it normal to resort to using superbuilds?

I’m writing an app, which is using blake3, libsodium, libwebsockets(git master, because I have a patch upstream not in the release yet to support IPV6 PREFER_SRC_PUBLIC), google test, and a custom library.

Libsodium uses autotools configure files.

All of these are a bit esoteric and not normally installed on Linux machines, if they are they are often old versions.

So I went with a superbuild.

From the main superbuild, to each ExternalProject_Add function
I am doing

And then for my app that needs to link all those libraries I am doing
“-DCMAKE_CXX_FLAGS=-I ${CMAKE_BINARY_DIR}/install/include”
It’s basically making everything build static libs in like a standard install prefix in the build directory, and letting all of the dependent libs install themselves into a prefix I can later use to link with and use the standard include path with.

Not all the dependencies use native cmake, so I figure it is more reproducible to do that and then use find_library in my main project that needs to link.

Is that like non-standard, or clean enough?

It seemed to be the cleanest thing to do.

If I was using popular libraries, like openssl or gnutls, I’d just use the OS’s libraries.

For my own lib, I am just using what add_library exported, not going back to find_library.

Superbuilds seemed to be a common way to support libraries that use cmake natively, and ones that don’t. They also happen at configure time, so you can build libraries before having a build dependent on them.

I see another thread about a package manager for CMake, CPM. Very related, yeah this is about managing dependencies cleanly.

And it acts as a wrapper for FetchContent.

So superbuilds=good for dependency management?

Superbuilds are (IME) good for packages. The ParaView superbuild is essentially its own distribution management system tailored to building ParaView binaries. Updating dependencies is tedious and time consuming (as it is for any distribution of disparate software), but it also means we’re in control of what is packaged.

Another use case that ParaView’s superbuild infrastructure supports is a “developer mode” (unused by ParaView itself) where it just builds dependencies and then offers a file to give to cmake -C to use those dependencies as the superbuild itself would build it. This can be used to do development against “obscure” libraries in a controlled way.

If the project is standalone, FetchContent-based solutions are good (though @craig.scott has more experience), but if it is intended to be used/distributed with other things or mechanisms, using find_package is generally more flexible as mixing vendored dependencies almost never goes well.

My experience with superbuilds is that it tends to be either limiting or accumulating options (“what if I want to pass this option to this dependency?”, etc). Also it’s a new abstraction layer that hides the below CMake project, and many users never even realize that they can build the project without the superbuild (which IMO is a bad point).

However, it is sometimes necessary to build the dependencies (e.g. for cross-compilation).

My favorite way is to provide helpers to manually build the dependencies (similar to what @dylanetaft describes with CMAKE_INSTALL_PREFIX and CMAKE_PREFIX_PATH), but keep the project “pure” using find_package. By default, it takes the system libraries, and if the user manually specifies CMAKE_PREFIX_PATH, it will take the local dependencies. I find it more versatile and easier to maintain. I wrote about it in more details here.