I don't think these two statements are fully compatible:
> Do any of the following apply to you?
> - You want to avoid hard-coding paths
> ...
> If so, you’ll benefit from a CMake-like build system.
and
> Don’t GLOB files
I find it very annoying that I, a human, am expected to keep a current list of all source code files, when listing files is something that computers are very good at. They even tell us how to structure the project with a src folder, but I still have to remember to add or remove source code files to the right CMakeLists.txt when the content of that folder changes. It's poor design.
Blanket advice always has edge cases. Re. whether GLOB'ing is good or bad, it pushes all the structure to your filesystem.
This is fine if your directories exactly map to binaries/libs/what-have-you, but what if you want unit tests for functions in each source file? Should you compile those with your binary? You could move them all into a tests folder and have a bin and a tests, but what if you want a test binary per source file (maybe your tests erratically take out their host process?)
The bottom line is that there has to be structure _somewhere_. If you're leaning heavily into CMake and your project isn't trivial, some of that structure may as well go into the build system.
DRY applies here. Placing files in a directory hierarchy and/or naming them according to a strict convention is "writing it down" once; repeating that in a text config file is writing it down a second time.
A good build system should use a concise but configurable rule to decide what to build as far as possible. Whether the details of that rule are "compile every file below this directory" or "compile every file that matches *.cpp but not *Test.cpp" (or some combination, or similar) isn't important.
Then it's the programmer's responsibility to "write the info" correctly by strictly conforming to that rule in how they name and place files, and/or tweaking the rule if necessary (e.g., "... But don't compile any file that has a period as the first character of its filename").
I've never respected the 'don't use GLOBs' recommendation with CMake and had practically no issues. They even added CONFIGURE_DEPENDS to ease the process.
To me, the arguments against using GLOBs here seem too constructed for modern C++ developers.
CMake gets a lot of hate because a lot of large projects use it poorly and the syntax is strange, but I've found it invaluable for projects I've worked on. The first page of this site has a great list of reasons why someone would want to use CMake. I would recommend at least reading that far rather than reading "CMake" in the title and reflexively commenting something negative. I skimmed through and this seems like a nice resource to get people spun up on CMake, I'll recommend it to new users in the future.
> CMake gets a lot of hate because a lot of large projects use it poorly and the syntax is strange
Sounds like a ”you’re holding it wrong”[0] defense. In my experience, it’s exciting to start using it, then you start pushing it and it’s annoying or simply falls down. I’ll admit I’ve avoided it for years now (maybe it needs a revisit), but I bought the book, I drank the koolaid, and I tried to like it. But imo it really is problematic, and I’m one of those people who’s since settled on basic (BSD) Makefiles.
Well, cmake is there because people did the same thing to autoconf, so it’s hard to be too sympathetic. Cmake is useful, but also terrible, like most build systems.
The difference I see is that Autotools is mainly useful for handling differences between various Unix variants, which isn't a concern now that commercial Unix isn't really a thing besides Mac OS X. CMake doesn't have the years of arcane knowledge about dozens of Unix variants that Autotools does, but it lets you target Windows using the native tooling as opposed to having to do all your development in Cygwin, and it automatically generates IDE projects so you can use IDE provided tooling like debuggers and profilers. Personally, I think this is a more compelling value proposition than what Autotools offers. Obviously, if you're fine with using a text editor and GDB and for some reason need to target SunOS, SCO UnixWare, 386BSD, A/UX, etc but not Windows, Autotools is great for that use case and you should stick with it.
Pretty much all newer languages massively simplify their build processes by establishing a bunch of common-sense conventions: the output directory is always the same, all *.foo files in a directory are automatically considered source files, the output is identical to the directory name, things like that. They also include proper versioning of dependencies instead of "just assume /usr/include/foo/foo.h is the version we want, yolo".
In principle it's very much possible to do all of this in C or C++, which would massively simplify stuff. But both C and C++ being a "design by committee" affair there will be endless fighting over which conventions to choose so I'm not holding my breath.
My experience is that CMake is fine (even great) for small to medium sized projects. Including dependencies, automating tests, even packaging is all handled with not too much fuss.
If you think your project will have more than half a dozen developers then you should probably start thinking about something like Bazel. But both have their idiosyncrasies and Bazel for a small project is overkill.
Is there an open source project which uses CMake well and could be used as a reference for good CMake practices?
I've been using CMake for years and it's definitely not the worst solution for building multiplatform C++ projects. But I've never read a CMake script and thought what a clean solution, it's always a bit gnarly.
We are trying to use CMake in a very limited fashion.
For example, any build time environment checks are forbidden (no "try_compile" scripts), and all configuration for all platforms is fixed.
We don't use it for installation and packaging; it is only used for builds. The builds have to be self-contained.
We also forbid using CMake files from third-party libraries. For every library, a new, clean CMake file is written, which contains the list of source files and nothing else.
From this standpoint, there should be no big difference between CMake, Bazel, Buck, GYP, GN, etc.
I don't know about "could be used as a reference for good practices", but here's a CMake file for a game project I've worked on if you're interested looking at how someone might use CMake for a smaller codebase (everything large I've used it for has been a work for hire unfortunately). It compiles on Linux with GCC, Mac OS X with Apple Clang, and Windows with MSVC, and supports multiple platform backends (currently SDL2 and SDL3). I've done development work on it with CLion, Xcode, and Visual Studio.
> But I've never read a CMake script and thought what a clean solution, it's always a bit gnarly.
I think using CMake for a cross-platform project that supports multiple compilers will always be a bit gnarly, mainly due to the differences between Windows and Unix-like platforms. MSVC is configured very differently to GCC and Clang so you have to list all your compiler flags twice, and there's no good option for doing system-wide installation of libraries (there's vcpkg, but a lot of stuff on there is missing or outdated) so you have to support both system-wide libraries on Unix-like platforms and user-provided DLLs on Windows.
For better or worse CMake has become a de-facto standard. You can (justifiably) argue Meson is better but honestly CMake is "good enough" and a slightly saner build system is not worth the loss of "standardization".
For C and C++, if I'm not using CMake, then I'm using some cobbled together shell scripts that do precisely what I need, no more, no less.
But for libraries there's a huge benefit to CMake since you can create a Config.cmake file for other projects to easily add your project as a dependency.
Or to use with FetchContent for rather nice dependency management.
The syntax is horrible, the semantics confusing, the way to do anything correctly weird and unintuitive like those generator expressions or whatever they're called and how the Config.cmake file is supposed to be generated. It is hot garbage.
But I'll still use it because the alternative is making dependency management in C/C++ land even worse.
Even latest CMake version still has that terrible syntax. If they want to survive the competition, at some point they need to provide (an option at least) another, proper syntax.
What’s the competition these days? I've never seriously used anything beyond plain old Make for my C/C++ projects, but that's more because they were dead simple and didn't justify the big-project features. What would someone use to build more complex things?
In the realms of Windows and game development you primarily use Visual Studio Solutions / projects with property sheets. The underlying build system is msbuild.
It is less powerful than CMake and has a relatively steep learning curve due to poor documentation. But once you get the hang of it, it's actually pretty straight forward with just a few pitfalls here and there. You simply have to accept that certain things are not possible... but chances are, these things can't even be done easily in CMake either.
FASTBuild[0] is super fast for large projects and comes with distributed builds and caching out of the box. It requires a bit of effort to set up, but it supports globbing sources, there's no separate generate build step, and it can also make Visual Studio solutions.
It would be nice if it just became a python interpreter. The concepts and build that CMake has is pretty good, but implementing it is a pain due to the quasi shell syntax.
Never going to happen. The kitware folks are aware of how bad the cmake language is, but they would rather corral it into a semblance of sanity (e.g. actual types rather than everything being stringly typed, eliminating the imperative stuff) than provide a different language.
Have to say I agree. Anyone who wants to use a different language should really look at a different build system. It would about the same amount of pain.
From last couple of years, VSCode's CMake Debugging plugin has made using CMake much easier. After using that I don't spend endless amounts of time figuring out what went wrong. This also helps me not to be afraid of CMake and I have started to like it.
I love cmake. It's a brilliant tool. I've used it extensively. In fact, I've recently reworked a large collection of applications and their C++ sources plus test units. The whole process was a joy.
I do wish some brave soul would update the main parser to support generator expressions directly so they can have whitespace. Then make the end* commands have optional parens and you've almost got a decent language.
I don't think these two statements are fully compatible:
> Do any of the following apply to you?
> - You want to avoid hard-coding paths
> ...
> If so, you’ll benefit from a CMake-like build system.
and
> Don’t GLOB files
I find it very annoying that I, a human, am expected to keep a current list of all source code files, when listing files is something that computers are very good at. They even tell us how to structure the project with a src folder, but I still have to remember to add or remove source code files to the right CMakeLists.txt when the content of that folder changes. It's poor design.
Blanket advice always has edge cases. Re. whether GLOB'ing is good or bad, it pushes all the structure to your filesystem.
This is fine if your directories exactly map to binaries/libs/what-have-you, but what if you want unit tests for functions in each source file? Should you compile those with your binary? You could move them all into a tests folder and have a bin and a tests, but what if you want a test binary per source file (maybe your tests erratically take out their host process?)
The bottom line is that there has to be structure _somewhere_. If you're leaning heavily into CMake and your project isn't trivial, some of that structure may as well go into the build system.
DRY applies here. Placing files in a directory hierarchy and/or naming them according to a strict convention is "writing it down" once; repeating that in a text config file is writing it down a second time.
A good build system should use a concise but configurable rule to decide what to build as far as possible. Whether the details of that rule are "compile every file below this directory" or "compile every file that matches *.cpp but not *Test.cpp" (or some combination, or similar) isn't important.
Then it's the programmer's responsibility to "write the info" correctly by strictly conforming to that rule in how they name and place files, and/or tweaking the rule if necessary (e.g., "... But don't compile any file that has a period as the first character of its filename").
All of my projects GLOB source files, headers, and test source with CONFIGURE_DEPENDS. Haven’t had a problem yet.
Yet you won't find a CMake best practices text that won't mention how bad globbing is.
You don't notice it on an SSD but glob hits spinning drives hard.
I've never respected the 'don't use GLOBs' recommendation with CMake and had practically no issues. They even added CONFIGURE_DEPENDS to ease the process.
To me, the arguments against using GLOBs here seem too constructed for modern C++ developers.
CMake gets a lot of hate because a lot of large projects use it poorly and the syntax is strange, but I've found it invaluable for projects I've worked on. The first page of this site has a great list of reasons why someone would want to use CMake. I would recommend at least reading that far rather than reading "CMake" in the title and reflexively commenting something negative. I skimmed through and this seems like a nice resource to get people spun up on CMake, I'll recommend it to new users in the future.
> CMake gets a lot of hate because a lot of large projects use it poorly and the syntax is strange
Sounds like a ”you’re holding it wrong”[0] defense. In my experience, it’s exciting to start using it, then you start pushing it and it’s annoying or simply falls down. I’ll admit I’ve avoided it for years now (maybe it needs a revisit), but I bought the book, I drank the koolaid, and I tried to like it. But imo it really is problematic, and I’m one of those people who’s since settled on basic (BSD) Makefiles.
[0] https://www.cnn.com/2010/TECH/mobile/06/25/iphone.problems.r...
Well, cmake is there because people did the same thing to autoconf, so it’s hard to be too sympathetic. Cmake is useful, but also terrible, like most build systems.
The difference I see is that Autotools is mainly useful for handling differences between various Unix variants, which isn't a concern now that commercial Unix isn't really a thing besides Mac OS X. CMake doesn't have the years of arcane knowledge about dozens of Unix variants that Autotools does, but it lets you target Windows using the native tooling as opposed to having to do all your development in Cygwin, and it automatically generates IDE projects so you can use IDE provided tooling like debuggers and profilers. Personally, I think this is a more compelling value proposition than what Autotools offers. Obviously, if you're fine with using a text editor and GDB and for some reason need to target SunOS, SCO UnixWare, 386BSD, A/UX, etc but not Windows, Autotools is great for that use case and you should stick with it.
Pretty much all newer languages massively simplify their build processes by establishing a bunch of common-sense conventions: the output directory is always the same, all *.foo files in a directory are automatically considered source files, the output is identical to the directory name, things like that. They also include proper versioning of dependencies instead of "just assume /usr/include/foo/foo.h is the version we want, yolo".
In principle it's very much possible to do all of this in C or C++, which would massively simplify stuff. But both C and C++ being a "design by committee" affair there will be endless fighting over which conventions to choose so I'm not holding my breath.
My experience is that CMake is fine (even great) for small to medium sized projects. Including dependencies, automating tests, even packaging is all handled with not too much fuss.
If you think your project will have more than half a dozen developers then you should probably start thinking about something like Bazel. But both have their idiosyncrasies and Bazel for a small project is overkill.
> If you think your project will have more than half a dozen developers
https://github.com/llvm/llvm-project/blob/main/llvm/CMakeLis...
How many active contributors does LLVM have? Hmmm
https://github.com/pytorch/pytorch/blob/main/CMakeLists.txt
How many active contributors does PyTorch have? Hmmm
https://github.com/boostorg/boost/blob/master/CMakeLists.txt
How many active contributors does boost have? Hmmm
I could go on...
Is there an open source project which uses CMake well and could be used as a reference for good CMake practices?
I've been using CMake for years and it's definitely not the worst solution for building multiplatform C++ projects. But I've never read a CMake script and thought what a clean solution, it's always a bit gnarly.
https://github.com/ClickHouse/ClickHouse
We are trying to use CMake in a very limited fashion.
For example, any build time environment checks are forbidden (no "try_compile" scripts), and all configuration for all platforms is fixed.
We don't use it for installation and packaging; it is only used for builds. The builds have to be self-contained.
We also forbid using CMake files from third-party libraries. For every library, a new, clean CMake file is written, which contains the list of source files and nothing else.
From this standpoint, there should be no big difference between CMake, Bazel, Buck, GYP, GN, etc.
I don't know about "could be used as a reference for good practices", but here's a CMake file for a game project I've worked on if you're interested looking at how someone might use CMake for a smaller codebase (everything large I've used it for has been a work for hire unfortunately). It compiles on Linux with GCC, Mac OS X with Apple Clang, and Windows with MSVC, and supports multiple platform backends (currently SDL2 and SDL3). I've done development work on it with CLion, Xcode, and Visual Studio.
https://github.com/nfroggy/openmadoola/blob/master/CMakeList...
> But I've never read a CMake script and thought what a clean solution, it's always a bit gnarly.
I think using CMake for a cross-platform project that supports multiple compilers will always be a bit gnarly, mainly due to the differences between Windows and Unix-like platforms. MSVC is configured very differently to GCC and Clang so you have to list all your compiler flags twice, and there's no good option for doing system-wide installation of libraries (there's vcpkg, but a lot of stuff on there is missing or outdated) so you have to support both system-wide libraries on Unix-like platforms and user-provided DLLs on Windows.
ParaView [0] and VTK [1] are big projects from the same shop that does CMake.
[0] https://github.com/Kitware/ParaView
[1] https://github.com/Kitware/VTK
LLVM's CMake build has had lots of love poured into it.
The beman project: https://github.com/bemanproject/exemplar
KDE's stuff (which is the original reason CMake became popular and adopted) remains updated and fairly clean.
Been reading it. Sure, it is a nice resource.
...
I am going to keep on using xmake
https://xmake.io/
For better or worse CMake has become a de-facto standard. You can (justifiably) argue Meson is better but honestly CMake is "good enough" and a slightly saner build system is not worth the loss of "standardization".
For C and C++, if I'm not using CMake, then I'm using some cobbled together shell scripts that do precisely what I need, no more, no less.
But for libraries there's a huge benefit to CMake since you can create a Config.cmake file for other projects to easily add your project as a dependency.
Or to use with FetchContent for rather nice dependency management.
The syntax is horrible, the semantics confusing, the way to do anything correctly weird and unintuitive like those generator expressions or whatever they're called and how the Config.cmake file is supposed to be generated. It is hot garbage.
But I'll still use it because the alternative is making dependency management in C/C++ land even worse.
Even latest CMake version still has that terrible syntax. If they want to survive the competition, at some point they need to provide (an option at least) another, proper syntax.
What’s the competition these days? I've never seriously used anything beyond plain old Make for my C/C++ projects, but that's more because they were dead simple and didn't justify the big-project features. What would someone use to build more complex things?
Meson and Bazel are the primary contenders in CMake’s market, as far as I can tell.
In the realms of Windows and game development you primarily use Visual Studio Solutions / projects with property sheets. The underlying build system is msbuild.
It is less powerful than CMake and has a relatively steep learning curve due to poor documentation. But once you get the hang of it, it's actually pretty straight forward with just a few pitfalls here and there. You simply have to accept that certain things are not possible... but chances are, these things can't even be done easily in CMake either.
FASTBuild[0] is super fast for large projects and comes with distributed builds and caching out of the box. It requires a bit of effort to set up, but it supports globbing sources, there's no separate generate build step, and it can also make Visual Studio solutions.
[0]: https://www.fastbuild.org/docs/home.html
I like xmake, it's fast and it's a lua file, so no DSL
https://xmake.io/
Either CMake or Meson. If you never felt the need to move beyond Makefiles, Bazel is almost certainly too complex.
It would be nice if it just became a python interpreter. The concepts and build that CMake has is pretty good, but implementing it is a pain due to the quasi shell syntax.
These guys had a competition way back and settled on python.
https://www.scons.org/
Never going to happen. The kitware folks are aware of how bad the cmake language is, but they would rather corral it into a semblance of sanity (e.g. actual types rather than everything being stringly typed, eliminating the imperative stuff) than provide a different language.
Have to say I agree. Anyone who wants to use a different language should really look at a different build system. It would about the same amount of pain.
Yea no. Syntax is just that, syntax.
If anything is holding back CMake, it's the strongly typed core.
Nevertheless, CMake is simple. There currently nothing convincingly better for the general case.
> Yea no. Syntax is just that, syntax.
If that was the case, Gradle wouldn’t move from Groovy to Kotlin.
Related. Others?
An Introduction to Modern CMake - https://news.ycombinator.com/item?id=39784784 - March 2024 (28 comments)
An Introduction to Modern CMake - https://news.ycombinator.com/item?id=22577889 - March 2020 (41 comments)
An Introduction to Modern CMake - https://news.ycombinator.com/item?id=17897685 - Sept 2018 (122 comments)
Modern CMake is Bazel or Gradle.
takes cover
From last couple of years, VSCode's CMake Debugging plugin has made using CMake much easier. After using that I don't spend endless amounts of time figuring out what went wrong. This also helps me not to be afraid of CMake and I have started to like it.
cmake --trace-expand + your terminal's built-in search works quite well, too
I love cmake. It's a brilliant tool. I've used it extensively. In fact, I've recently reworked a large collection of applications and their C++ sources plus test units. The whole process was a joy.
I do wish some brave soul would update the main parser to support generator expressions directly so they can have whitespace. Then make the end* commands have optional parens and you've almost got a decent language.
It is a noble attempt
But they should support more native languages
D Odin Rust Nim C3 Freebasic Freepascal
I'm switching all my C projects over to the Zig toolchain, and honestly, I'm not looking back.
You’re switching to the build system of a different, pre 1.0 programming language that has frequent breaking changes?
Zig has a built-in C compiler, arguably one of the just-worksiest C compilers out there.
The Zig build system is basically cmake except worse but in Zig.
yeah CMake has been good to me past few years.