Posted by signa11 3 days ago
> 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.
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.
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").
As an example of file system and build system disagreeing, what if you start writing a new source file that you don't want to compile and link until it's ready and needed? If globbing file names is the source of truth, either the uncompilable draft of your new file has to be placed in some inconvenient alternate location where the build system doesn't pick it up, and later moved to a regular source folder, or it breaks the build until you waste enough time to hack together something that can be compiled (or even compiled and linked).
An IDE can use actual file lists and its internal project configurations to manage file references in build scripts (e.g. silently updating build scripts when files are renamed or moved or offering a good UI to add source files to build targets).
What makes using a different top-level directory (which is something that would work with nearly all build systems) inconvenient? But if you really want these not-to-be-compiled-yet files nearby in the directory tree, customise the rule to, e.g., not compile anything having a filename beginning with IGNORE, or in a subdirectory called IGNORE, or (if you want to get fancy) listed in a .compileignore file (a la .gitignore). If the number of exceptions is small, then explicitly marking them in any of these ways is DRYer (= requires less maintenance) than explicitly recording the larger number of "business as usual" cases a second time.
> An IDE can use actual file lists and its internal project configurations
If you rely on an IDE to this extent, it's effectively part of your build system. (Which is absolutely fine, and has some benefits, though also some downsides.)
Provided that the IDE is "doing the work for you" (e.g., by adding each new .cpp file you create to the list of files to compile, until you uncheck a box somewhere that removes it), you're good. The only questions then are (1) whether this IDE configuration info is handled sanely by the VCS, and (2) whether it's readily understandable and usable to someone without the IDE (e.g., a CI build server). If the IDE stores this info in an externally defined plain-text build script like a Makefile or Maven pom.xml, then the answer to both questions is probably "yes" -- but, depending on how expressive the build script language is, it might be difficult or error-prone for the IDE to be able to update it reliably if the user makes their own changes to it as well.
Ultimately what I’m trying to say is that msbuild has a good set of default globbing rules and it’s very easy to fine tune.
To me, the arguments against using GLOBs here seem too constructed for modern C++ developers.
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...
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.
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.
CMake was created in 2000 with large, cross-platform projects involving multiple organizations in mind. The development was funded by the National Library of Medicine (NLM) for the ITK project (https://github.com/InsightSoftwareConsortium/ITK). As of 2025, ITK consists of millions of lines of code and has received contributions from hundreds of developers. Other projects of similar scale include VTK and ParaView.
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...
While it might look like technicality the number of projects that kept their own build systems is substantial. I would say there are dozens open and many more not so open build systems out there that support large projects and organizations. I don't see anyone rushing to "rewrite it in CMake" anytime soon. At least in gamedev where I work.
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/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.
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.
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.
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.
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.
If that was the case, Gradle wouldn’t move from Groovy to Kotlin.
takes cover
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)
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.
that! that!!! If someone replace top level with anything (even Python) I would be a lot happier