You can handle The Diamond with CMake
beza1e1.tuxen.de
beza1e1.tuxen.de
macro(find_package)
if(NOT "${ARG0}" IN_LIST as_subproject)
_find_package(${ARGV})
endif()
endmacro()
[0] C++Now 2017: Daniel Pfeifer “Effective CMake"
https://www.youtube.com/watch?v=bsXLMQ6WgIk&vl=en
slides: https://github.com/boostcon/cppnow_presentations_2017/blob/m...We shouldn't have to do macro/function overrides, but I'm glad they exist in CMake. Current status quo is "it's really annoying and a huge headache to do, but at least CMake allows me to do it, unlike others".
I think it's minimal work, as I would like to have all my libraries imported and linked anyway:
find_package(MyLib)
target_link_libraries(${PROJECT_NAME} PRIVATE MyLib::MyLib)
This looks tidy any more explicit, avoids transitive leakage and more.What is it that does not often work as you expect?
root/
base/
a/
b/
As long as root has a CMakeLists.txt and includes base, a, and b, cmake will figure it out. After the build tree has been built with cmake, you can even run make from the build/root/a/ directory and base/ and a/ will be built. The author, for some reason, does not want a cmakelists.txt in root/ and wants to run cmake from a or b alone so they have to use a more convoluted approach.Of course, the root CMake also needs to parse all sub directories which requires time. However, this has not been a big problem in practice so far. Only on the "annoying" level.
Are there any reasonable raw make based solutions for VC++?
- you don’t target different platforms
- you don’t target different compilers
- you don’t cross compile and need to maintain multiple toolchains
- you like juggling different compiler flags
- you work on small codebases
- you haven’t bothered to sit down with cmake for the few hours needed to understand why a cmake project might be more maintainable than a make one
- I do not target nor care about Windows systems (which is in agreement with the GP I guess - cmake may be useful as to get project files for VC covered.)
I agree with the GP post and:
- I do target different platforms (Linux, BSDs and Solaris)
- I do target different compilers (GCC, clang and ICC)
- I do cross compile and maintain multiple toolchains (I have 16 GCCs and 8 clangs on my box)
- I do juggle different compiler flags
- I work on a codebase with 1.24M lines
- I have experienced the pain of working with dependencies we need that use cmake.
The only thing cmake has given me is having to learn another way to do all the same things I already know how to do for 10 other build systems.
NB: I do not agree with the GP post that make by itself is a good tool. In most cases, the best tool is simply the most popular one, which - sadly, because it's a pretty bad tool - is autotools for my use cases. I know how to deal with it, my co-developers know how to deal with it, and my users know how to deal with it. Even if I learn 10 other build systems, that still leaves my co-developers and users hanging.
If we disregard this and go purely with technical merits, my personal opinion (= I don't care to argue for this, feel free to ignore) for the best tool becomes Meson.
(I'm aware this post is a bit condescending, but I feel that this is an appropriate response to the parent post's equal condescension. ["You probably feel the way you do because:" - really? What gives you that "insight"?])
[libtool isn't even actively maintained! Like, seriously, I'd guess >90% of packages on any Linux/BSD systems use it in their build and it's essentially abandonware!]
I have years of experience with cmake, and all the other things you mentioned. I work in environments where the correctness of the binaries is important, and cmake fights that at every possible step. The documentation is poorly organized and overly verbose. 99% of the details in the docs are irrelevant 99% of the time, and the important details are missing or relegated to a non-discoverable page elsewhere on their site.
That is where CMake really shines - it takes the headache out of managing complex, sprawling builds once you get it setup, and you won't have to keep tweaking the config to manage every other dev's system. I grant that the documentation is not perfect and there is a significant time investment in getting everything 'just so', but the long-term time savings make up for that completely in my experience.
Most of my career has been in non-mainstream software development. There's always a ton of rare, unique, or broken stuff that I have no control over. In many situations, adding an extra layer via a generator (such as CMake) adds more work than convenience.
I've also found CMake's documentation to be useless most of the time.
By the time I track down and fix all the issues, I might as well have just written a Makefile (or whatever obscure, ancient build tool they use).
RUN tar -C opt xvf /…/foo-1.2.3.tgz RUN <build the thing if needed>
And a few lines like this in make:
INC += $(wildcard /opt/foo-*)/include LIBS += -L…
This generalizes elegantly to subdirectories. (See “Recursive Make Considered Harmful” for the right way to do it), and (crucially!) it won’t build if you don’t have the build environment set up correctly.
It also lets you prevent the CI build container from talking to the internet, so outages of upstream server infrastructure, or package-manager-du-jour serving bitcoin miners can’t directly break production binaries.
It also generalizes to building in other people’s operating systems, etc.
With cmake, you need to read the documentation for find_foo, which invariably has a different calling convention than find_bar (if it exists at all), and it will try to look places it should not for the library. Adding a library this way takes hours.
Also, the docker + make approach works well with languages other than c/c++; cmake does not, unless cmake happens to include built-in support for the language in question.
(Ed.: Also I agree the cmake docs are atrocious.)
????
This is a serious (though slowly becoming a non-issue) problem since compilers encode source file names into the output binary's debug information and strings (__FILE__). The "-fdebug-prefix-map=old=new" GCC option and its cousins are what is slowly making this a non-issue, but those are a relatively recent addition.
But yeah, figuring out what linker options were generated or even how to version a project takes a lot of digging into the bowels of cmake or a couple laps through the documentation.
You can certainly run regular Make in Windows, either natively or in one of the unix-ish environments, and have it call CL.EXE?
Having it emit VS/MSbuild projects is more of a problem, since MSBuild itself is rather like Make with different terminology and a strange pre-existing library.
You can easily make Make targets that download stuff too; although it gets messy if it needs to work everywhere, because there's no http downloader in posix, so you're at the mercy of the environment.
The windows folks buy into it.
Other than that, it is much more procedural than make and matches the way people write code.
Make is not procedural and is full of footshoots for the uninitiated. You need to write one big dependency tree, and it is defined out-of-order with very opaque rules for things like variable expansion.
With cmake, you can just write your CMakeLists.txt from top-to-bottom with if statements, indenting and understandable variables.
Not to mention that people hate build systems. They have the same popularity as taxes, which everyone wants to get in, get out and forget until next year.
That said, cmake is better for well-defined projects - you can lose the time saved on the front end is eaten up later when you want to do something fancy like change the compiler or compile an external library with unsupported flags.
In this meta project I either just add_subdirectory all necessary components manually, or I use Findxyz.cmake scripts for each module with that "header guard". They check if target is already defined, if not they add_subdirectory the required component.
For each component (i.e. cmake project) I can then just find_package (module mode) all dependencies and target_link them. Like any other dependency.
For consistency reasons I like to put cmake find modules for all external (installed) library as well and then let the find modules use find_package in config mode for installed libraries, and add_subdirectory for my own components, or for projects I want to include with source and build along with the other stuff. This way I have one point where I can control the source of all dependencies.
Another way would be to just install all components as libraries in the system with proper cmake config files for find_package in config mode. I dont like to clutter my system with all kinds of application specific libraries, so that is not the way for me.
https://cmake.org/cmake/help/latest/command/include_guard.ht...
It is 2021, and we still have C++ developers program in a language which lacks namespaces, types and relies heavily on generated variable names.
How did we end up there?
Initially my draft had the title "CMake and the Diamond". When the article was finished, I decided to make it more clickbaity because I'm not Paul Graham or Scott Alexander. Rumors are that words like "How to" and "You" make it look more interesting. I weakened it intentionally by adding the "can" because I don't claim that this approach is the one best solution.