How to handle duplicate targets (add_subdirectory only)

Here is the situation.

We have library p which depends on a
We have library d which depends on a
And finally library p depends on library d

p -> d -> a
  \
    > a

Our company is basically only using add_subdirectory since we need to build everything from source.

How are we supposed to handle this situation? Currently the cmake code is feels a bit hacky.

We have logic like this inside our cmake code:

if (NOT TARGET A)
   add_subdirectory(A)
endif()

What’s the correct solution? Is this what we are supposed to do?

And this is why I don’t recommend add_subdirectory as a third party dependency management mechanism :slight_smile: .

Options:

  • Rip d/a out and force d to use p/a instead
  • Edit d to support being told "a exists $here, use it"

Using unedited vendored projects requires that the project supports being vendored without edits. These…basically don’t exist, so you’re left with either making the project support that or editing your vendored dependencies.

What’s the alternative besides add_subdirectory?

I’m aware of FetchContent which could work. But we cannot use it.

For monorepos (generally best suited to products, not things that make SDKs or the like)? Not much. For “normal” projects? Just use find_package and provide external scaffolding to get dependencies (Conan, Anaconda, vcpkg, Linux packages, whatever).

What I would recommend is that every package have its own top-level build and then you toposort them and build them in the “right” order (though if everything is target-based and you’re not relying on cache variables or global properties for inter-package communication, you can just build them in any order these days). So each package shouldn’t embed anything and instead they get pulled out into the top-level so they can be properly deduplicated.

@craig.scott may have other suggestions.

This really sounds like a problem FetchContent would solve for you rather easily (dealing with common dependencies is one of its primary capabilities). You’ve stated that it isn’t an option for you though, so I respect that there are reasons why you can’t use it. Any other suggestion I would offer would probably just amount to manually implementing what FetchContent already provides out of the box.

I would very much like to use FetchContent.

But currently there are 3 main reasons we cannot use FetchContent at our company.

Until these 3 issues get fixed we cannot use FetchContent for our large scale code repositories. (All 3 of these issues essentially boil down to speed but in different ways)

1.) Windows performance. Although I know this is getting addressed for 3.21 (https://gitlab.kitware.com/cmake/cmake/-/issues/21703)

2.) We really need the ability to not pull down everything a repo has to offer. Some of our codebases have huge amounts of documentation we don’t want to pulldown. Example say a repo looks like this:

$> ls
src/
docs/
examples/
CMakeLists.txt

We really really don’t want to pull down the docs/examples.

3.) We need a FetchContent source cache. This issue is discussed here: