
Modern CMake short tutorial and best practice - radomir_cernoch
https://codingnest.com/basic-cmake/#fnref6
======
wheels
It still boggles my mind that CMake was the thing that won for the next
generation of build systems. Its syntax is probably even worse than bash
(except that it didn't have decades of compatibility to blame that on), making
it the single worst language I've ever written code in. I struggle to
understand how one could sit down to create a language and come up with
something that bad.

There was apparently at one point effort to replace its frontend with Lua, but
that sadly never took off. (I, like everyone else, still hold my nose and use
it, but man ... like ... bwa?)

~~~
pornel
The problem I have with CMake & autotools is that every project writes their
build process from scratch, and everyone does things slightly differently
making their project a snowflake.

Certain platforms, compilers, and dependencies need special handling and
workarounds, and this knowledge isn't shared and has to be repeatedly
rediscovered by every project.

Rust's Cargo, perhaps accidentally, has found a fantastic solution to this.
The build process itself is built out of packages. I don't have to know how
configure MSVC compiler, I just use an existing crate for it. I can use
existing package to find and configure libpng — the work to handle its own
quirky pkg-config replacement has been done once, and now every downstream
project can benefit from it.

Package manager in the build system may sound strange, but it's actually
fantastic. It works so well that I switched my C projects to Cargo to be able
to build them on Windows.

~~~
ridiculous_fish
Your critique is fair for autotools but not CMake, which encodes a ton of
platform and compiler specific knowledge [1]. cmake modules can also be used
to find libraries, e.g. FindPNG for libpng.

1: [https://cmake.org/cmake/help/v3.11/manual/cmake-compile-
feat...](https://cmake.org/cmake/help/v3.11/manual/cmake-compile-
features.7.html#supported-compilers)

~~~
predakanga
The bundled Find* modules are great, but once you go beyond what's included
you often run into problems.

In converting a recent project to CMake (so as to use CLion), I encountered
all of the following:

\- find_package modules that didn't support REQUIRED or VERSION options

\- Lack of standardization on whether find_package variables include recursive
dependencies

\- Some modules supply imported targets, some don't - requires different
consumption

The fact is, I can't simply rely on find_package doing what I expect - I have
to read each and every Find*.cmake I use.

On top of that, there's a bunch of conflicting information out there - should
your source list include header files? Some older blog posts suggest it's
necessary for dependency calculation, newer ones don't seem to mention it; I
honestly have no idea what the right answer is.

~~~
maccard
> should your source list include header files?

IMO yes. Part of CMakes usefulness is IDE integrations, so listing your
headers means they show up in your IDE.

As for your points about find modules not working, that’s not really CMakes
fault. I’ve spent days fixing bad autotools/premakr/scons/custom scripts, (not
to mention third party code in general).

~~~
predakanga
I agree that it's not CMake's fault, but it does fit the grandparent's
statement that "everyone does things slightly differently".

It's also very fixable - a central site for collecting find_package modules
and enforcing certain standards should be doable, and would improve CMake's UX
significantly.

~~~
maccard
Yeah agreed, a site of find package modules would go a long way towards
helping, but only if they’re vetted in some way, otherwise nothing r ally
changes.

------
gravypod
While CMake is verbose I don't think it's terrible. The complexity of building
a simple flat source project vs a multi-stage build project scales very well.
One thing that amazed me was how simple it is to script up the creation of
generated libraries.

For school I had to generate a build step that would allow me to embed Duktap
and link to a library called Glad. Both needed to be generated with a python
script. Doing this was dead simple [1]. I didn't have to do anything crazy to
locate the path to python, the source folders I want to output into, etc.
CMake has magic for that.

Granted I spent just as much time reading the really horrible docs to find out
how to use these magic features as it took me to complete the assignment. I
think like many amazing technologies it's blocked by a steep learning curve
and a long long history of worse ways of doing things. Every time you google
for how to do something you'll get an answer from a different part of the
project's lifespan.

[1] - [https://github.com/gravypod/solid-
snake/blob/master/CMakeLis...](https://github.com/gravypod/solid-
snake/blob/master/CMakeLists.txt#L22-L41)

~~~
therealjumbo
This is a great introduction to CMake for a single project/repo. You could add
a section on installing the project. Although that probably isn't required for
the student projects.

What I'm still searching for is a nice way to build multiple repos, which have
dependencies on each other, in a cross platform way, that's distribution
friendly. What I mean by distribution friendly is: you shouldn't be bundling
your dependencies.

But it's nice to compile your own dependencies and link to that for debugging.
So I want something that easily allows the upstream developers to download,
compile and debug their dependencies, but doesn't do that when distributions
(Debian, Fedora) go to compile and release your code. And it'd be nice if this
was part of the CMake, so new dev's don't need to run some bash script to get
setup, or follow a long list of instructions in a document to download,
compile and install said dependencies. I'd also like to keep my existing CMake
as horrible as it is at times, I'm not sure if switching to Meson is really
worth the effort. Especially since I'm still stuck with C/C++ anyways.

~~~
pfultz2
Daniel Pfeffier’s effective cmake talks about how to do that. Setup each
project standalone and get the dependencies with find_package. Then create a
superprojects thats add each dependency with add_subdirectory and override
find_package to be a no-op for dependencies added with add_subdirectory, since
the “imported” target is already part of the build:

[https://m.youtube.com/watch?v=bsXLMQ6WgIk](https://m.youtube.com/watch?v=bsXLMQ6WgIk)

Now, overriding a builtin function is not the best idea so in boost cmake
modules we have a bcm_ignore_package function which will setup find_package to
ignore the package :

[http://bcm.readthedocs.io/en/latest/src/BCMIgnorePackage.htm...](http://bcm.readthedocs.io/en/latest/src/BCMIgnorePackage.html#bcm-
ignore-package)

~~~
therealjumbo
Just watched that video, long but awesome. I'm not sure on how exactly his
strategy is supposed to go, but it sounds like the superproject is another
repo. I don't really want that. What I'm thinking right now is to use the same
overall idea, but instead of a superproject, I use the same repo, and use:
[https://cmake.org/cmake/help/latest/module/ExternalProject.h...](https://cmake.org/cmake/help/latest/module/ExternalProject.html)

But put that behind a flag, that defaults to off, so it doesn't interfere with
the distributions. And it supports patching so I can patch whatever
dependency's CMake, in the event that they don't (yet?) support the same
strategy so it can work transitively without having to boil the ocean.
Granted, you would need to write patch files, for basically all your deps
initially because nobody else is doing it that way.

For reference, I'm trying to help convert an existing open source project from
autotools to cmake, but at work I'm the resident cmake expert, and we have
multiple products, which effectively are built as different 'distributions'
(not always Linux based) so I've been searching for a good way to do this for
a while, even though I just started contributing to OSS.

------
striking
If you liked this piece, please also consider reading
[https://pabloariasal.github.io/2018/02/19/its-time-to-do-
cma...](https://pabloariasal.github.io/2018/02/19/its-time-to-do-cmake-
right/), which is a modern (2018) guide to what you should and should not do
in CMake.

It's very enlightening.

------
Sir_Cmpwn
Honestly CMake is just as bad as it's predecessors. Today I use meson, which
isn't perfect but is far better than CMake.

~~~
AlexMax
How does meson's find_library compare to CMake when it comes to finding
libraries on Windows? That, and the very nice GUI tool that comes with it are
my two killer features of CMake.

~~~
Sir_Cmpwn
I don't use Windows and don't care about Windows, you'll have to see for
yourself.

~~~
AlexMax
Thought so.

If you're primarily a *NIX user, there are a bunch of reasonable build system
alternatives out there, but if "supporting Windows" is even a little bit of a
priority, I have found CMake to be far and away the least painful way to build
multi-platform software on Windows, especially if you're a novice.

Until Meson even approaches being as easy to use on Windows, I feel like it's
going to continue to be second fiddle.

~~~
Sir_Cmpwn
It may be second fiddle for people who care about Windows, but thankfully they
seem to be few and far between these days. There's no reason to support
Windows when they won't even pretend to care about standards compliance.
Although even then, having to draw the ignorant masses of Windows devs out of
their GUIs would be quite a task.

~~~
tom_
I don't know how common I am, but literally everybody that's ever paid me to
do any development work has wanted support for Windows. (I'm a bit worried
that this might date me, but my first time coding for coins was 20 years ago.)
Tools that don't support Windows are just not useful to me.

I'm not even sure which standards you're worried about, either. VC++ supports
C++14 pretty thoroughly, and you've got various builds of gcc or clang if you
want whatever extra support they give you. We're long past the dark days of
yore when support was hit or miss and MS didn't give a shit.

If you want POSIX, you are out of luck, but that's fine, because Windows isn't
a Unix, so POSIX doesn't apply. My view is that this is actually a pretty good
thing, but reasonable people may differ - e.g., by thinking that this is a
fantastic, amazing, excellent and extremely good thing ;)

Overall: the really weird thing is that Windows support is no harder to
arrange than support for any other platform. But because it's so unpopular
with a certain brand of nerd, it's somehow OK to just code for POSIX, complain
when your code doesn't build on Windows, and blame the whole affair on
Windows. But, dear people that do this, I'm afraid you appear not to have
noticed that portability is _your_ job, not platform vendors'.

~~~
Sir_Cmpwn
>But, dear people that do this, I'm afraid you appear not to have noticed that
portability is your job, not platform vendors'.

That's rich. CPU manufacturers ship C compilers and virtually every operating
system other than Windows provides a POSIX interface (and in fact, so did
Windows at one point!).

Consider the math; if the platform supports portable interfaces then the
vendor did a small amount of work to support a large number of programs. If
the programs support the platform then a large number of programs have to do
the work to support a single platform.

Windows is quickly losing what little relevance it has left. Today's most
relevant platform is the web and end users are accessing it on their phones -
and we both know how Windows for phones ended. Windows Server is a bad joke
and the servers which power the platforms of today are run on Unicies and
programmed by engineers on Unicies. Windows is dying quickly and is simply not
important anymore. What few end users they have left are being driven away to
Chromebooks and Macbooks by unwanted updates, advertising, and a non-stop
barrage of annoying bullshit.

------
dewyatt
The generator expression used here isn't necessary:

add_test(NAME plain-run COMMAND $<TARGET_FILE:vector-test>)

You can just use:

add_test(NAME plain-run COMMAND vector-test)

Since the <COMMAND> is an executable target.

------
dewyatt
I also found this to be a decent reference (mostly the videos):

[https://gist.github.com/mbinna/c61dbb39bca0e4fb7d1f73b0d66a4...](https://gist.github.com/mbinna/c61dbb39bca0e4fb7d1f73b0d66a4fd1)

Also, some of the Kitware projects on GitHub.

------
Koshkin
While I appreciate the need for DSLs, in many cases I feel like I'd rather use
an API. At least, an API imposes no constraints on the features of the
language itself. (So, imagine a DOM for VS solutions and projects; writing
your own script would be a breeze...)

------
waynecochran

         Make is horrible because it does not handle spaces in paths
    

Putting spaces in pathnames is idiotic to begin with. I have written Perl
scripts that are 3x more complicated just to deal with spaces in pathnames.
You might as well blame Make for not handling UTF-32 characters in pathnames
-- I mean, someone might want to put a GREEK CAPITAL REVERSED DOTTED LUNATE
SIGMA SYMBOL (U+03FF) in their path -- Make would suck if it didn't allow
this.

You missed the most egregious problem with Make -- the fact that commands must
begin with a friggin' TAB! No other whitespace will do. Think of every manual
writer who has to somehow convey that the whitespace you (don't) see is a TAB.

~~~
minitech
> Putting spaces in pathnames is idiotic to begin with.

The thing that’s idiotic is not being able to handle valid paths. Yes, all of
them. Yes, including ones containing GREEK CAPITAL REVERSED DOTTED LUNATE
SIGMA SYMBOL (but also apostrophes, backslashes, quotes, dollar signs). It
boggles the mind that a tool that does everything through a shell can’t be
made to pass arbitrary information to that shell in a reliable manner.

~~~
spc476
GNUMake handles UTF-8 just fine. I was able to replace spaces (character 0x20)
with non-breaking spaces (character 0xA0) and it just worked
([https://news.ycombinator.com/item?id=16527084](https://news.ycombinator.com/item?id=16527084)).

~~~
minitech
Sorry, didn’t mean to imply it couldn’t; just pointing out that it’s important
to handle despite the way the parent suggests it’s a ridiculous expectation.

------
armitron
CMake is absolutely terrible if not destructive for non-trivial projects that
require frequent build system updates and restructuring (especially those tied
to cross-compilation).

GNU Make might have its share of issues but compared to CMake, it's dead
simple and I've never once in 20 years of programming encountered a build
issue I could not debug.

CMake has had me throw up my hands and give up in despair far too many times.
It boggles the mind that people continue to use such a rotten tool. Probably
because it looks attractive, superficially, but if one examines it in more
detail any possible justifications for using it should completely fall apart.

~~~
ridiculous_fish
How do you use create a portable non-trivial project with make? How do you do
discover what libraries, headers, functions, etc. are available?

~~~
mcguire
That's what the autotools are for. Yes, they're horrible. The problem is
horrible and the autotools are worse. But they work.

~~~
ridiculous_fish
Autotools do work, but what autotools user can sneer at cmake? So I guess OP
is using something else, which is why I asked.

------
gkya
I'd suggest everyone to check out bmake. It has a very nice standard library
of makefiles with which often your makefile can be a couple lines long, one of
which just includes the relevant library.

~~~
izacus
It's it fully compatible with CMake and will consume thousands of CMake built
C/C++ libraries out there?

------
ddavis
I think it’s recommended to avoid set(...) in modern/best practice cmake
because it’s global. Use the target_* functions.

~~~
therealjumbo
The author does discuss this in a footnote: [https://codingnest.com/basic-
cmake/#fn4](https://codingnest.com/basic-cmake/#fn4)

In this case, I think I agree. Otherwise ya, try to avoid set. Except for
CACHE variables, I think.

