On 06/20/2017 11:56 PM, Niall Douglas via Boost wrote:
I'll ask again: do you have any reference to those statements? The official cmake docs seem to disagree about this specific case.
The cmake docs are updated on a git pull request basis by volunteers as and when someone feels they need to be updated. They are, in general, woefully out of date. And as I mentioned here before, Stephen never finished the cmake3 documentation effort he began due to changes in personal circumstance (returning home to Ireland).
To be honest, and forgive my skepticism, I find this to be not a good sign of something that is to suppose to dominate the industry: - There is no real documentation available on the "best practices" you advertise, they are told based on anecdotes - Kitware is indeed a company who offers support and consulting and claims to maintain CMake. Not saying that you (or Stephen Kelly) don't know what you are talking about...
Why should the root cmake be responsible for configuring a specific sub module. This is asking for cluttering everything into one single entity in a (almost) totally unrelated place. Why not keep it local to where it should be used.
<snip>
In the cmake I mocked up we are separating concerns: "how to build this library" from "how to configure this library". It is like the intended difference between the SConstruct and SConscript files in scons: one file says what needs to be built and how they relate declaratively, the other file sets flags, optimisation, settings according to the target system etc imperatively.
I can see that and certainly see the benefit of it. I think there is a great chance to further drive the modularization with that approach. I guess the consensus would be (each of those file local to the module being built): - CMakeLists.txt: Describe targets declaratively - Dependencies.cmake: Describe dependencies (PUBLIC, PRIVATE, INTERFACE) - Install.cmake: Define the install logic - Setup.cmake: Define Options etc. and eventually call add_subdirectory to have the CMakeLists.txt being processed This would seperate concerns, the root CMakeLists.txt would then include the corresponding Setup.cmake and Install.cmake files. This would be how the authors envision that the module should be built. Third parties can either follow that approach or derive their own logic. The advantage of the advise to have everything clobbered in the root CMakeLists.txt would be: - it is scalable (Library authors don't need to ask for permission to have some options etc. added inside the root) - the information would be local to the package being built, that is every user can directly observe which different build options are available - Each package can be used standalone or within the super project. The imperative logic is split, and can be reused on a by need basis without copy&pasting from one large, central CMakeLists.txt Does this sound like a suitable approach?
In exactly the same way as with scons, the non-root CMakeLists are the declarative structure describing a build graph. The rootmost CMakeLists are the imperative commands transforming some subset of that build graph into a build according to local conditions.
That's the way you ought to be ideally speaking writing your cmake3. I don't know what else I can explain here. As Peter mentioned, this is 101 elementary build system theory here. It's nothing cmake specific, though because of how variable scopes usually begin with CMakeLists.txt, it does strongly encourage the imperative logic to go into the rootmost layer rather than somewhere more logically placed. But it is what it is.
Interesting that you bring this up ... If you are so much in favor of a declarative build system, we already have multiple. For example plain old make or Boost.Build. With that being said, wouldn't it be more viable to improve Boost.Build with: - Better Documentation - More examples - Tight integration with CMake: * Have Boost.Build generate XXXConfig.cmake and XXXTargets.cmake files * Have a CMake module that drives b2 This would make everyone happy, wouldn't it? People who prefer declarative builds can stick to Boost.Build. Boost.Build would stop being "asocial" to CMake based projects.
One concrete example: I have networking layer which supports different transports, say tcp, libfabric, MPI and infiniband verbs. The user of my library doesn't really care about which one is used, so ideally, I would give him a compiled version of my library with the internal settings I believe are best for him. In that case, my network layer module would be configured with one of those options. Naturally, I would think, those options are defined within this very sub project. The root project really doesn't care, nor do I want that this implementation detail leaks into the users CMakeLists.txt.
Would you consider that bad practice to begin with? How would you solve this?
It depends on if you ever expect unknown third parties to want to do custom builds of your codebase. If you don't, your approach is fine, and it's easier. If you do, then you need to separate the declarative stuff from the imperative stuff. Then unknown third parties can reuse your declarative stuff, and skip or ignore your imperative stuff as they are doing a custom build.
There are tons of other use cases very similar to that. Having everything clobbering up in the root Cmakelists.txt doesn't sound appealing to me.
You obviously don't place ALL your imperative logic in the very rootmost CMakeLists. There are natural root points where build config markedly changes when you go up a directory level. That's where you locate the imperative logic. If higher level cmake wants to ignore your imperative logic, it skips over the directory with the CMakeLists with the imperative logic and adds the subdirectories of the inner directories directly, thus bringing in only the declarative parts only. So you "reach in" two directory levels to skip the imperative logic.
Does this make more sense now?
Thanks for the explanations. It does indeed make more sense now. I hope I could summarize it correctly and provide a nice consensus.
Niall