
Everything You Never Wanted to Know About CMake - pmoriarty
https://izzys.casa/2019/02/everything-you-never-wanted-to-know-about-cmake/
======
sysprogs
Turns out I'm not alone who spent considerable time fighting with CMake
configuration issues. I ended up patching it to support a basic debug server
protocol, so you could step through the CMakeLists.txt files in a debugger. In
case anyone's interested, here's the CMake fork with debug support [0] and
there's a detailed tutorial [1].

[0] [https://github.com/sysprogs/cmake](https://github.com/sysprogs/cmake)

[1]
[https://visualgdb.com/tutorials/cmake/debugger/](https://visualgdb.com/tutorials/cmake/debugger/)

~~~
mdaniel
Have you talked to the CLion team about this, whether to integrate it into the
product or make a plugin for it (if such a thing is possible)?

Given how CMake centric CLion is, and how abusively dumb the CMake DSL is,
your project sounds like a great thing to teach CLion about.

------
dfgdghdf
CMake is awful, and the fact that the C++ community seems to be settling
around it will be a massive disadvantage in the long run. Why do you think so
many WASM examples use Rust? It's because Cargo is a sane build system that
makes cross-compilation easy.

Issues with CMake:

\- The DSL is not very good. It needs proper functions, for one.

\- Non-hermetic builds (also mentioned in this thread)

\- No ability to easily query the build DAG

\- Headers are not modelled properly (they should be a dictionary of paths ->
paths, not a list of include directories)

\- Build folders cannot reliably be used between configurations, leading to
confusion and cache misses

\- Everything is convention driven. It does not model the build graph
properly.

\- Globs do not work properly (maybe that has changed recently?)

\- The cache is not portable across a network, or even between folders on the
same machine

The C++ community deserves better!

Others in this thread have mentioned some modern alternatives:

\- Buck Build (Facebook, Uber, AirBnB)

\- Bazel (Google)

\- Pants (Twitter)

\- Please (Thought Machine)

It doesn't actually matter which of these succeeds. They all model the builds
in such a way that you can easily transpile between them.

~~~
Teknoman117
Cargo (and Rust tooling in general) still have an annoying design flaw in my
mind -> they all pretend that system package managers don’t exist.

Admittedly with rust not having an ABI you’d still have to rebuild all the
packages whenever the compiler was updated, but I’d like to see rustup support
toolchains installed to the system.

~~~
Rusky
This is a rustup issue, not a Cargo issue. You can use a system-installed
Cargo/rustc just fine.

~~~
steveklabnik
rustup link can point to a system installed rust, so should be fine!

------
ryanianian
CMake suffers from a lack of conventions. If you want to follow some set of
conventions and have one directory for your headers and another for your
sources and another for your tests etc you still end up having to write a
hundred lines or more of error-proned CMake.

As ugly as things like Maven and Gradle are, if you follow all of their
conventions they get out of your way.

In addition:

\- no cross-platform way to require a compiler version or way to set compiler
flags

\- linkers are not abstracted from you (good luck trying to get cross-platform
support for relocatable binaries; cmake really bungles up rpath etc)

\- also good luck trying to link both static and dynamic libraries and targets

\- third-party libraries aren't really a first-class thing

\- no simple way to specify "build all these things into the target directory
with a conventional file layout"

80% of projects need to deal with these and not a lot else. Why is there not a
list of "follow these conventions and you don't really need to write or look
at cmake file"?

(To be fair: compilers, linkers, and OSes share some blame in not being more
consistent but the goal of a competent build-system is to handle these things
for the 80% case.)

It doesn't _really_ matter how terrible the syntax and internal implementation
are if most reasonable use-cases don't need to look at it.

~~~
stefan_
They recently added a convention: target-based CMake!

Now, because they are switching to what every sane build system was 5 years
ago, _but over the course of 10s of 3.x versions_ , your CMake buildfiles are
about to explode in complexity and frankly, stupidity. Lots of "does this
target exist already because I need to support some older version alongside
bleeding edge" and usually, if not, just copy that stuff over from their HEAD.

Everything they touch goes terrible. We just need to stop with like version
3.10, pretend CMake is dead and slap a DEPRECATED warning at the beginning.
It's the only way out.

------
daenz
I want to upvote this for solid info, but I want to flag it so fewer people
have to know about CMake. It blows my mind that experienced software
developers would develop yet another pseudo-language DSL vs creating a
portable library (python, java, go, whatever) that has a sensible interface.

~~~
whatshisface
I'm not sure what you mean. If for whatever reason I need to set up a big
C/C++ application so that it can compile on diverse systems, CMake is one of
my best bets for getting that to happen. If anyone would like to suggest a
better option I would gladly switch, but as it stands I think CMake is pretty
useful to know about.

~~~
daenz
I'm not arguing that it doesn't do what it was designed to do. I'm arguing
that it is a grotesque DSL with bizarre design choices. CMake would have been
better off as a cross-platform library that didn't re-invent concepts that
other programming languages have already gotten correct, and instead focused
on making the build system be cross-platform.

~~~
bch
I’ve mentioned the same thing before and ironically, CMake was developed in
the course of building medical imaging software[0] that was using Tcl, a
flexible, embeddable language, but then decided to roll their own ad hoc
language in this case. They left a LOT on the table with that decision, I
think.

[0] [https://en.wikipedia.org/wiki/VTK](https://en.wikipedia.org/wiki/VTK)

------
mrich
The syntax of cmake is not very elegant, and error-prone. It also does not
scale to large projects, configure times go up quickly. The documentation also
leaves much to be desired.

That cmake is in the place it is nowadays is a reflection of the state of
cross-platform build systems 10 years ago, and possibly some marketing.

Interesting alternatives worth checking out: Meson and gn (the latter can be
used for building llvm)

~~~
entelechy
I'd like to add Buck and his siblings Bazel & Pants to this list. Buck is used
by large companies like Dropbox, Facebook and AirBnB.

Additionally I want to mention my company released also a package manager that
uses buck as a packaging format:
[https://github.com/LoopPerfect/buckaroo](https://github.com/LoopPerfect/buckaroo)

And so far over 320 libraries have been ported to buck and are maintained by
our bots: [https://github.com/buckaroo-pm](https://github.com/buckaroo-pm)

------
AlexandrB
I don't get the hate for CMake. It has several useful advantages over plain
makefiles:

* It's meant to be cross-platform, so a well structured CMake file will work in Windows.

* CMake modules allow you to include source-based libraries without a lot of drama. This is especially useful for cross-compiling or embedded use-cases.

* Out-of-source builds are supported with no additional work on my part. This is great for CI generating debug, release, and other build variants from a single cloned repository.

~~~
klodolph
Comparing anything against plain Makefiles is a really low bar. Makefiles have
been around since 1976. I think the hate for CMake comes from other directions
entirely. The CMake scripting language is especially bad. Out-of-source builds
were never really that hard in the first place. I think people hate CMake
either because they were doing something slightly more unusual than CMake
tolerated, or because they hated CMake's language (which is very easy to
hate).

CMake has simply sucked less, overall, than the competitors of its time, for
most common build tasks. That era is over, now that the new generation of
build systems is here (Bazel, Buck, Pants, Please).

~~~
codeflo
I wonder how Makefiles got such a bad reputation. After decades of reading how
bad Makefiles are, I recently tried writing one for a moderately complex
build, just for fun. I was pleasantly surprised: make is very fast, it's well-
documented, and the execution model is so simple that I found it very easy to
figure out how to script the things I needed to do. I'm not saying it's great,
but there are a lot worse build systems around that people still actively
advocate (cough msbuild).

~~~
klodolph
Over the past 30 years or so, build tools for C and C++ programs have
converged to what you see today.

For example, with GCC you can use the -M options, and then you can -include
the result in your makefile. This was not always possible. You had to manually
specify the .h files for every .c, and if you omitted one, you could get a
successful build but a broken program!

Then consider the process of building a shared library, which is different on
different platforms, but if you restrict yourself to GCC on Linux with GNU
Binutils it’s damn easy.

There are a few other, minor failings of Make. But you’re right, it’s very
comprehensible and straightforward. I still say that it’s a low bar, though,
by modern standards.

------
gumby
I feel dirty whenever I modify a CMake file but you, madam/sir, have opened
the gates of hell and had me peer in. _shiver_

~~~
SAHChandler
I'm always willing to help others pierce the veil of reality from time to
time. Hope you enjoyed the post :)

------
jarjoura
I used Makefile in university and that was simple enough until you needed to
do something more complicated and then it started to become unwieldly fast.
Back then, GNU was popular and automake/autoconf were attached to every
project. If you were building on a Linux system, it worked, but being a Mac
user, it always came with gotchas. It was quite a pain to use honestly and
required a very deep understanding of it's .m4 design if you wanted to do
anything clever.

Cmake seemed to work well, meaning, I could just open the GitHub project and
generate an Xcode project file and bam, it just worked. However, agreeing with
everyone here, holy hell, trying to start a new project with it, or migrate
another project over to it is quite a futile experience.

It has several escape hatches in the weirdest places. Need to pass a
conditional compiler flag? Good luck with that exercise. A compiler flag was
added from a previous macro and there doesn't seem to be a way to keep it from
doing that. I don't know, the whole system seems nice when you don't have to
touch the make file, it just works, but I can only imagine the hair splitting
effort it took to get that make file in a workable state.

------
zaphar
The thing I want most from a build tool is not a nice DSL or portability. What
u want most is hermetic builds. There are only a few told out there that do
it. CMake is not one of them.

~~~
nerdponx
What do you mean "hermetic" in this context, and why does CMake fail?

Also, for my purposes (using both Windows and GNU/Linux at work), portability
is a tremendous benefit.

~~~
klodolph
In this context, "hermetic" means that for every step in the build, all inputs
and outputs are known by the build system. Hermetic builds are reproducible,
and can be more easily done by a distributed build cluster. Hermetic build
rules can also be run in a sandbox, so you can have assurances that your build
scripts are correct.

With non-hermetic builds, it's much more difficult to verify the correctness
of your build rules, which means that you might get non-reproducible builds or
you might get incorrect incremental builds. With hermetic builds, it's always
safe to do an incremental build, no matter what state the repository is in.

~~~
01100011
In my experience, this is usually handled with a combination of source control
and containers(Docker, but more commonly just a chroot image).

~~~
klodolph
I'm not sure how source control is connected. Normally, I want hermetic builds
whether or not the files I am building are checked into source control yet.

~~~
01100011
Well if you want repeatability, I'm assuming that means over some long
timeframe, which means you need a way to get the code as it was at some point.

You then need to build that code using a consistent set of tools and
libraries, which is where the chroot comes in.

~~~
klodolph
Repeatability definitely does not mean some long time frame, it's over any
time frame. Short time frames have the largest impact, because it allows you
to use a shared cache for build products.

One big chroot around your whole build system isn't enough, you can run into
nondeterminism problems due to relative ordering of different build steps
during execution. This is why it's nice that the new build systems make each
individual step hermetic, because you can make a separate chroot for each
individual step (Bazel executes each build step in a separate sandbox).

This allows you to cache all intermediate results, share the cache, and get
the same results both for clean and incremental builds, repeatably. It's also
easier to remove nondeterminism from individual build steps rather than
looking at the whole build.

~~~
01100011
Cool, didn't know that. I like the idea of breaking things up into steps.

So you're saying this takes care of race conditions in the build where things
may happen out of order even if you have a fixed environment?

~~~
klodolph
Yes. Since the different steps are executing in different sandboxes, you can
be confident that there are no race conditions between them.

------
Teknoman117
I use CMake because most of the dependencies I consume use CMake, and tooling
for CMake is more widely available.

------
kstenerud
For those who just want to get something building in cmake, just start with a
template project and modify it.

[https://github.com/kstenerud/modern-cmake-
templates](https://github.com/kstenerud/modern-cmake-templates)

------
newprint
I tried to learn CMake 4-5 years ago and just gave up. Cumbersome, weird
......

------
ausjke
I have always been using makefile, not perfect, but good/solid enough for all
my use cases.

Maybe just use python/etc to write a better wrapper for Makefiles? There are
so many build system to choose these days.

------
eslaught
Is there a good way to debug CMake build rules (especially in the presence of
add_custom_command and friends)? I've got a case which builds differently in
Make vs Visual Studio---in VS, dependencies get messed up and some files don't
even get built at all (and then the build fails later on because those files
are missing). I can't for the life of me figure out how to debug this, it's
absolutely infuriating.

~~~
tom_
I've got by with some combination of the following:

\- message(STATUS ...) or message(FATAL_ERROR ...) for printing stuff out at
configure time, e.g.

    
    
        message(STATUS "libcurl found: ${HAVE_LIBCURL}")
    

\- cmake -E echo for printing stuff out at build time, e.g.

    
    
        add_custom_command(TARGET lorenz POST_BUILD
            COMMAND ${CMAKE_COMMAND} -E echo "lorenz command line:"
            COMMAND ${CMAKE_COMMAND} -E echo '$<TARGET_FILE:lorenz>' ${LORENZ_ARGS}
            VERBATIM)
    

\- diagnostic-level MSBuild output for dependency issues - you can configure
this in Visual Studio, Tools, Options, Projects and Solutions, I think (it's
around there somewhere). You might not expect much from MSBuild debug output,
considering how annoying the rest of it is, but it's actually extremely
comprehensive, and I've found it useful for figuring out even rather weird
stuff. There's quite a lot of it, though, so get a cup of tea

A passing familiarity with the MSBuild syntax might be helpful, but I've
managed to do mostly without.

------
purplezooey
It always pisses me off when I have to build a GitHub project or something
with cmake. Just use a Makefile and autoconf, dammit.

------
gaius
Every day I bitterly regret that they decided not to use Tcl for CMake in the
end.

