Hello,
My team and I really like the versatility of FetchContent
and we use it almost everywhere. We’ve built up a fair number of internal libraries to increase development speed, so things feel like Lego blocks now. That’s great, but it’s also brought me to a lot of problems with dependency handling. I see many projects using very different approaches (variables, custom functions, etc.) and I just can’t figure out what the best practice is supposed to be.
The main goal for us is not only to make things stable, but also to require the bare minimum work from the end user: ideally either FetchContent
or find_package
(and that’s it), unless they explicitly want to override something.
For context: we’re using CMake 3.30
.
I have a fairly complex dependency tree that looks something like this:
- LibA, LibB – external libraries (third-party).
- LibC – external library that depends on LibA and LibB (not ours).
- LibD – our library, depends on LibC and LibB.
- LibE – our standalone library (no dependencies).
- LibF – our library, depends on LibD.
- exec – final executable, links against LibD, LibE, LibF.
My questions are:
-
Where is it okay to use
FetchContent
, and where is it not advised?- Should
FetchContent
only appear at the top-level superproject (exec), or is it acceptable inside libraries (like LibD) to pull in their dependencies?
- Should
-
Is it possible to make this work without introducing options/variables in each subproject?
- For example, can I just write each library using
find_package
for its deps, and then let a superproject override everything withFetchContent
&&OVERRIDE_FIND_PACKAGE
, ?
- For example, can I just write each library using
-
What would be the needed order?
- If I want to use
FetchContent
in the superproject for everything, do I need to declare and make available A, B, C before D and F, etc.?
- If I want to use
-
Export sets and collisions.
- I often see errors like:
install(EXPORT ...) includes target "foo" which requires target "bar" that is not in any export set
the most apperent it is when building grpc using FetchContent for instance they go off after setting set(libname_ENABLE_INSTALL OFF)
- Handling SO name conflicts.
- If both my project and the superproject rely on (say) LibB as a .so with the same SONAME/symbols, what’s the right way to avoid collisions?
- Should I never export third-party libraries from my package along with my package?
- Should I let the superproject always own the third-party deps (No
FetchContent
in the lib)? - Or is it okay to re-export them, then what do i do if my
exec
wants other version always not forget updating the lib along theexec
?
- If both my project and the superproject rely on (say) LibB as a .so with the same SONAME/symbols, what’s the right way to avoid collisions?
In short:
- We want stability and a simple user experience (just
find_package
orFetchContent
, nothing more). If it’s possible. For instance setting up lib to usegRPC
caused so much errors and fiddling with vars it uses, usingset
oroption
with Forcing cache to fix them, and those may not show up for a really long time. - But I’m really confused about what is considered “best practice” for balancing
find_package
,FetchContent
, andinstall(EXPORT …)
. - Sometimes things work fine for months, then break with the smallest environment change.
Thanks for the help in advance