cpm-cmake / CPM.cmake

📦 CMake's missing package manager. A small CMake script for setup-free, cross-platform, reproducible dependency management.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

A B A Cyclic dependencies

markfoodyburton opened this issue · comments

(Probably been asked before, but I can't find doc's on this)
libA -> libB -> libA.
What I expect is that when libB is constructed, it will discover that libA is already being built, so will not attempt to re-add it.
What I see is that the add_library in libA will be executed twice and cmake will complain that the library is already being built.

Is there a solution to this apart from breaking the cyclic dependency by hand?

Thanks

Imho this sounds like a won't fix issue on first thoughts. However, the use of cyclic dependencies should probably be a use-case that CPM should detect and provide a more useful error message.

This sort of issue, if CPM could somehow satisfy, will likely just peel back a layer of underlying issues that cyclic dependencies would cause. You should handle this in the same manner as how you solve cyclic header-includes and separate out the re-used code and/or add interface abstractions and use dependency inversion/injection principles etc.

Also, to cover more CPM related concerns as to what logic would even resolve a cyclic dependency? Then lets start with what if we included versions to the dependencies. This would be a developer nightmare as you may build either libA or libB as the top-level project and end-up with hard to discern package versions being used so unit-testing would be potentially broken. (see Limitations- First version used)

  1. libA@1.0.0 -> libB@1.0.0 -> libA@2.0.0
  2. libB@1.0.0 -> libA@2.0.0 -> libB@1.0.0

Actually in this case it's not a 'true' cyclic dependency.
I believe this works 'ok':
If libC depends on LibA and libB, and libA depends on libB, libC will pull in the required libraries (A and B), and (it seems) cmake will correctly allow libA to make use of libB and visa-versa. Both libA and libB are 'dependencies' and cmake 'finds' them when required for libA/B.

The issue is only when we attempt to build e.g. libA. Since libA is not a dependency of libA, cmake (seems to) add it as a dependency of libB and attempt to pull it in, even though libA is the current target that is being built. This is the dependency that causes the issue.

I'd expect that if e.g. libB depends on libA, and libA is currently being built, that libB's "dependency" for libA would be special cased and 'point back' to the library being built. Clearly this might be tricky to achieve. Of course (as you suggest) there are invariably ways around the issue (worst case introduce a libC).

The issue as I understand is that when configuring libA alone CPM is not aware that libA is already included in the project. When configuring libC and libA is added using CPM then it is aware that libA already exists and doesn't attempt to add it again within libB.

I think the only solution to configure libA would be somehow make CPM aware that libA is already available. One way could be to call CPMRegisterPackage(libA <libA Version>) in libA's CMakeLists before adding any dependencies.

Makes sense, but that would then break libA when it was used elsewhere? - Otherwise, why isn't this the default behaviour?

Makes sense, but that would then break libA when it was used elsewhere?

I don't think so, it should only change the behaviour to act as if libA as added by CPM itself in the standalone case.

Otherwise, why isn't this the default behaviour?

How would CPM know the name of the current project? We could infer it implicitly, e.g. from the PROJECT_NAME, but I'm not a fan of that option as build behaviour shouldn't change based on the project name.

Confirm this works and is a reasonable solution - THANKS for the help