From QMake to CMake: A story on why CMake is pretty nice in 2020
screen-play.app
screen-play.app
I rambled about this recently: https://news.ycombinator.com/item?id=24203172
The issue you linked to was changing the environment after running the "configure" step, but the availability of the header that had been newly installed was cached to "not available", so re-running cmake used the cached "not available" instead of checking the system again. Or something like that. Seems like a classic trade off of speed vs correctness. Something like SCons would always check everything and ran super slow, whereas CMake opts for not checking things that are less likely to change and caching the result. That shouldn't be used as a reason to throw away all the other good stuff CMake has :-)
I gave up. CMake is just too frustrating to work with. Hard to debug, different API versions, many different ways to do the same thing, hard to understand the abstraction of CMake itself. Total time sink. I will never use that again.
This is a huge problem with too many things - CMake included. Backwards compatibility is often being praised too highly.
CMake you run to generate files that will build your thing in a single configuration (platform, runtime settings, etc).
Visual Studio wants to have a solution where you can switch between many configurations (platforms, runtime etc).
Yes, I have looked into latest Visual Studio CMake integration but it doesn't suit my needs because it puts the build files in some arbitrary hidden random location on disk.
add_custom_command(TARGET project POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
some.dll
$<TARGET_FILE_DIR:project>)What's hard about that? You have a subproject per folder, in each subproject you specify the build targets and dependencies, and then you just specify build precedence in downstream libraries when need.
Exactly which problems did you experienced getting this to work?
This a language an evil genius might come up with while trying to do good.
Of course it sucks when you actually need something nontrivial.
Saying this as a heavy CMake user with a strong love and stronger hate relationship with it.
Doesn’t CMake not even have a concept of arrays? I remember reading that functions to generate arrays were really just fancy string concatenaters with quote escaping.
All this is just a gentle slap on the wrist for programming in your build system. Or doing something weird like optimizing a CMake-language JSON parser for performance [2].
1: https://cmake.org/cmake/help/v3.16/module/ExternalProject.ht...
That's an awfully polite way of saying Yes, the CMake scripting language is indeed terrible.
It's meant to be used for complex package-detection logic. CMake Find modules can run hundreds of lines. It's not unreasonable to expect proper support for arrays, proper functions that can return values, a sane syntax, etc.
I've been using CMake for around a decade and I have to say I never experienced anything remotely similar to what you described.
Do you mind sharing a real-world example of something that you struggled with while working with CMake?
To revisit the points I made in the old thread [0]:
We pay a price for fragmentation; it's better we all agree on one system. (Although I think it's fair to say that autotools has had its time in the sun at this point, I don't see a big problem in using it for Unix-only projects.) CMake can work very well, with impressive portability, if a project's CMake scripts are rock-solid. I plan to continue to use CMake, despite its considerable shortcomings.
If CMake did the following things, I could probably forgive the atrocious scripting language:
1. Overhaul the documentation. Every unit of documentation should be accompanied by an illustrative example; dry rambling essays are not the right format. Do what Microsoft do with their .Net documentation.
2. Official design-patterns for common tasks like Find modules. They shouldn't each have a character of their own. These scripts ought to be very consistent, very boilerplate-driven, and very boring.
3. Rework the officially bundled CMake scripts so that they all follow the current best-practices to the letter. As it stands, there's nowhere to go to see what real CMake scripts are meant to look like.
I would suggest a linter/improved errors and warnings, but I don't know if that's practical. I would suggest replacing the entire scripting language with something less awful, but it wouldn't do to throw out compatibility. Stability is a must-have for this kind of thing.
https://doc.qt.io/qbs/index.html
(Unfortunately it has been deprecated)
Builds are not hermetic/reproducible. When doing deep change of CMake files, the only reliable way to verify it is correct is to clean and rebuild everything.
CMake is very very slow. A minor change in CMake file results in CMake re-evaluation which is very slow for large projects.
CMake language is just pain.
And CMake does not have cache. CCache helps a little, but it’s still much worse than cache of proper modern build system.
I would probably recommend Bazel for building C++. Works like magic. Although if you hit Bazel limitations (likely you won’t but still), you don’t have many options how to deal with it. You never need to clean Bazel cache whatever you are doing (unless you are intentionally trying to break it of course).
I agree. And if you let me, I want to add that for small projects it is ridiculous overkill.
This leaves no reasonable use case for cmake (except maybe building itself solipsistically for all eternity). And I like it that way.
* each build file is independent (unlike CMake where the project is one big script with global scope and side effects)
* build script evaluation is hermetic (cannot read time for example) and strictly reproducible
* target is rebuilt not by timestamp changes (which is unreliable for several reasons), but instead all dependencies must be declared, and target is rebuilt when the hash of dependencies change
I don't understand your point. CMake projects specify build targets per module, and it's up to the developer to choose what he thinks should be a module. What value do you see in specifying build targets per file?
I say when cmake file includes another cmake file, all variables are inherited, and in certain cases cmake file can define variable in outer cmake file scope.
Thus build files are dependent on each other. Inclusion order is important, and when project refactoring/restructuring, it's a common error, that resulting project is incorrect because for example, some file relied on the fact that when it is included, certain variables or macros are already set by someone else. This makes large project maintenance quite inconvenient. Especially because CMake does not guarantee that error will be revealed unless it is full clean rebuild.
This is not true at all. You only "inherit" variables that you intentionally want to pass to downstream projects. You need to explicitly and intentionally declare your variables with PARENT_SCOPE to access variables from other files. That's an explicit decision from the developer.
Yep, most project variables, except for some locals.
> You need to explicitly and intentionally declare your variables with PARENT_SCOPE to access variables from other files.
No you don't. PARENT_SCOPE is needed to write variables into parent scope, but not read.
> That's an explicit decision from the developer.
Even so. Yes, when working very carefully, when you are a single developer writing on a small projects, and enforce code style, it is possible to keep the mess manageable.
When at least 100 people work on the project, if a project is several years old, if half of the team changed, CMake project is always a mess. (Source: I worked in two very different large companies which were using CMake; both of them eventually migrated away from CMake).
Someone might blame developers for making that mess. I'd prefer blaming CMake, as I see the same developer keep projects in good shape when using Bazel (or another build system with similar model).
You're talking about an entirely different thing then. Subprojects do inherit scope, but that is only relevant if you intentionally, and for some reason, want to reuse variables that were inherited. This is often used to propagate config settings from the project root to all submodules, but you only do this if that's something you explicitly want to.
> Even so. Yes, when working very carefully, when you are a single developer writing on a small projects, and enforce code style, it is possible to keep the mess manageable.
It really isn't. If you're dealing with a problem where random team members break stuff without oversight or awareness of what they're doing, and if this happens so often that you start to deflect blame at tooling, then you're not dealing with a build system problem. You're dealing with poor software engineering practices that not only goes way deeper than the build system but also cannot be fixed by replacing it.
> When at least 100 people work on the project, if a project is several years old, if half of the team changed, CMake project is always a mess.
It really isn't. What's always a mess under those circumstances is unmaintained code developed without any care or oversight, which just builds crust by accretion. The choice of build system has no influence on that outcome, not does the problem stay circumscribed to the build system.
As the saying goes, bad workers always blame their tools. Well, we can extend that saying to badly managed teams.
Yes it does. When build system prevents shooting in the foot (by being very strict about side effects of submodules in particular, and forcing to explicitly declare dependencies), the result is better project quality.
> As the saying goes, bad workers always blame their tools.
Sure. Just let's not jump to conclusion that everyone who criticise tools is bad worker.
I would not even grant this, and I spent a lot of time wrangling both CMake and autotools many years ago.
(Even if it does not generate makefiles, the project model is makefiles: targets, file names, timestamps.)
Because of side effects, when single cmake file modified, the whole project need to be regenerated.
Also, because of side effects, project files become too frigile, for example, changing the order of two includes may result in incorrect project definition.
And makefile generation part is problematic: using timestamps to check if something need to be rebuilt is unreliable (may result in not rebuilding something when project is actually changed).
I don't think it is. On Windows at least, it's still easier to build a VS project links with libraries and everything linked by editing a text file than it is to go through the GUI, and VS is worth using as a debugger if nothing else.
That's out of date; VS is adding C11 and C17. https://devblogs.microsoft.com/cppblog/c11-and-c17-standard-...
Quick googling shows there is a lot of external rules for pkg-config https://github.com/cherrry/bazel_pkg_config or rules for building third-parties with cmake https://github.com/bazelbuild/rules_foreign_cc for example.
And that was both time consuming and frustrating.
1) Every time some "guide" gets posted on the programming communities, there's people pointing out mistakes, deprecated functions or alternatives.
2) If you look at the CMakeLists.txt files of various popular libraries, they all look different. There's some commonality but much less than what you would desire from a more or less standard build system.
But cmake is a joke really. Overly simplistic, but for anything close to a real project compared to autotools, autotools is much much better. With autotools you get tons of constantly improving standard probes and fixes. Cmake is only for really trivially small projects.
I just added cross-compilation support for my aarch64 phones. It was doable with cmake, but it's a huge mess. With autotools it would have needed no changes at all. For cmake I need seperate CmakeCFG files. Not even talking about adding GPU support. Or valgrind. Or proper testing.
CMake is not the best build tool for C++, in a technical sense (pretty much every hash-DAG based tool like Qbs or Bazel is better), but it is the most common tool, which has little to do with merit.
I've compared it to other alternatives (though I wrote this ages ago and it needs an update): https://geokon-gh.github.io/hunterintro.html
I haven't used vcpkg - but the fact it's forcing its own toolchain file is a huge red flag.
CMake is actually really not bad as long as you stick to a subset of the language (ha! sounds like C++) and just don't deal with the whole installer side of things and generator functions. That said 99% of CMake files are written incorrectly. CMakeLists.txt should be system agnostic and compiler/toolchain agnostic. It just coordinates the linking of libraries and where to find headers. All system specific magic needs to be in the toolchain files. Once you break that rule things quickly spin out of control and you get crazy CMake configuration that are a nightmare to maintain
I see this repeated over and over since years. At some point if everybody is doing it wrong, either the tool is the actual issue, or there is a clear problem of miscommunication somewhere.
If I recall correctly, even the official Find... modules rely on deprecated functionality.
Basically you just fork Hunter and add your own package with it's own URL and hash and then it works. Just take a look at how the other packages are organized and it'll quickly make sense. You point your project at your fork instead of the "official" Hunter repo and voila. I ended up having to do it for a couple packages and there was no problem
I think there was a bit of a mental block for me at first b/c it feels like you will need to "maintain" your own fork. But this is dependency management - there is fundamentally no maintenance. You only update things when you decide to update dependencies. This is always a manual and deliberate process. It's just like how your Hunter version doesn't just magically grab the latest version of Hunter. It's also pinned to a certain release version number
The system is very well thought out..
- It is the industry standard
- Dependency managers like vcpkg and conan use it
- Is what Qt selected after killing Qbs
We can discuss the many problems of CMake but in the short and medium term, the best solution is to focus on improving CMake
Point is, stick to a tool and improve it, as you say, we don't need fashion in build systems as well.
There are a lot of C/C++ build systems until you need PCH.
1: https://onqtam.com/programming/2019-12-20-pch-unity-cmake-3-...
I don't think there's any point in using CMake if you're not forced to support Windows.
MOC is not a pre-compiler but a code generator; all code written by the developer is compiled as is, but it can include macros known to MOC for which additional code files are generated. This goes without notice when e.g. using qmake or cmake. No reason to hate it.
There was a reddit AMA when someone asked what the dev would change about CMake if back-compat wasn't and issue, and the dev said that hadn't thought about it. To me, that's the nail in the coffin: all engineers should think about how things ought to work, even if they cannot immediately fix it.
Just use Meson and leave this POS behind.
This has always been a bone of contention between me and my peers or leads: I always intended to drill down to the root of the problem to understand how a system should behave, while most of the time they considered it a waste of time and frustrating. "Patch with a workaround and call it a day" mentality is ubiquitous nowadays, from what I see.
Building multiple executables is not possible in a single .pro file, so if you've got a folder full of unit tests, which are separate applications, you have to create a .pro file with subdirs as the author here does, and then create another pro file for each test executable.
I’ve used CLion and PyCharm as my primary IDE but never used CMake on them anymore after a few aborted attempts. While not the IDE’s fault, CMake is that bad, starting with a “build” subdirectory. The bug report I filed with JetBrain/Idea (IDE creator of PyCharm/CLion) to make it work with Make/autotool is now the oldest bug over there.
I’ll take Make and autotool/autoscan any day and StackExchange for package identification of weird dependencies.
That's what people call "modern CMake".
The scripting language remains awful, though.
The language is terrible, but there are things CMake does pretty well.