IMO CMake is perfect for large, minimally configurable projects. It handles dependencies for incremental builds well (though maybe that’s more the build system (make, ninja, etc.) than CMake itself), and I personally enjoy using it.
However, once people start getting clever with it, it quickly becomes incomprehensibly complicated.
It is damn hard to write tooling for C++ projects: There are no standard ways to do anything, so you need to add lots of options just to cover a range of project configurations. Those add a lot of complexity very fast, which is unrelated to the actual task you want to solve.
CMake is bad and only usable if you write your configuration perfectly.
I don’t agree. I find CMake to be hands down the best build system generator out there, and allows the cleanest, most straight-forward projects you can think of.
The problems with CMake generally come from inexperienced developers not doing what they are doing and thinking that CMake is some kind of Basic-type scripting language, and in the process making a big old mess of everything they touch.
As a rule of thumb, if any of your CMakeLists.txt file has an if() in it, odds are you messed up already and should reconsider what you are doing.
I really could not disagree more. CMake is a terribly designed build system.
One of the most crucial things for a build system is to find and link dependency libraries. How does CMake do this?
Each CMake release distributes its own set of library-specific (!) find_package modules. These change between releases, sometimes in backwards breaking ways. Providers of some of these libraries don’t always like how the central CMake find_package module work, the options they provide, etc. and thus provide their own, incompatible, find_package modules. On top of this, some software package systems, e.g. vcpkg ALSO provides competing find_package modules, also incompatible and sometimes with exactly the same name. And, of course, sometimes no such module is available, and the developers of the software using the library provide their own, or just side-step the find_package system .
The end result is a total mess that can sometimes be nearly impossible to untangle without knowing exactly what environment the developers themselves use. Furthermore, you want to build older releases? Forget that, unless you are prepared to hunt down the exact versions of cmake, vcpkg, and libraries that the developers used at that point in time to get hold of the magical combination of find_package modules that actually builds the package. This is work of far higher complexity than matching semantic version strings, which is what we typically do with other build systems.
My impression is that the only people who like CMake are people who use it to build their own software and thus never run into this issue since it obviously works in their own environment that they’ve adapted the CMake build rules to. But, put someone else’s complex multi-dependency CMake project in their hands and ask them to build a 4 year old version and (1) watch them give up and (2) observe them then claim those “other” CMake developers just don’t know what they are doing. (When, in reality, the exact same problem applies also to their own software).
I have many more gripes with CMake than this, but I guess this rant is already long.
Each CMake release distributes its own set of library-specific (!) find_package modules.
This is a gross misunderstanding and misrepresentation of CMake. CMake ships with CMake configure scripts for some popular libraries, but their goal is to lower the barrier to entry to CMale by supportinh projects which haven’t been properly onboarded onto CMake by their maintainers.
Think of it as a the-chicken-and-the-egg problem, but tied to CMake’s adoption. You want to use CMake but you can’t just properly consume a dependency because it doesn’t export it’s own CMake config script, so what do you do? CMake supports those cases by means of user-provided CMake Find modules, which each project has to roll out on their own and ship along with their source code. That sounds like a lot of repetitive work, doesn’t it? So why not provide CMake-provided scripts with the distribution?
My impression is that (…)
If the worst point you can make about CMake is the cmake config scripts it bundles, then I’m afraid you are very opinionated over irrelevant details that are immaterial to the discussion.
This is a gross misunderstanding and misrepresentation of CMake.
You lead with this, but all I see in your reply is an explanation of why it works exactly the way I described. That explanation apparently it is: “because not everyone in the ecosystem acts the way CMake developers would want them to”, which I read as “CMakes design for external dependencies is broken.” The fact is, where we are right now, today, the find_package/config script design in CMake frequently makes resolving external dependencies far more painful than it already is without that complication.
That sounds like a lot of repetitive work, doesn’t it? So why not provide CMake-provided scripts with the distribution?
I believe my previous reply gives a very clearly answer: because we get stuck in a situation with multiple competing providers of ‘scripts’ that compete against each other and may apparently all change in backwards breaking ways between releases of CMake, releases of the library in question, and releases of package managers like vcpkg who have taken on themselves to fill in missing scripts.
In the end, it is me who have to dig deep into someone else’s mess of CMakeLists.txt:s to try to unravel the mystery of what feature identifiers they expect from that external depedency and how they expect them to work in the find_package/config script the developers happened to use at the time, so I can then go on an archaeological expedition through all possible providers of that config - including older versions of CMake itself to track it down. Multiply with the number of dependencies - which sometimes is up in the hundreds - and it should be clear why this is a horrible, horrible design of a build system.
If we had to stay with CMake’s broken design for this, at the very least it would need some form of declaration of dependency config scripts with a provider name and a semantic version. That way the problem would at least be solvable.
However, it is more or less only CMake that has this issue in its attempt to be so ‘meta’ about everything. Libraries already come with a build-system independent way to specify features: they are split over several library files and software using that dependency choose which ones to link with - using file names. This works well in nearly every other build system. I don’t get the motivation to try to abstract this into an interface that (for the reasons we’ve discussed above) introduces another intermediate dependency layer.
If the worst point you can make about CMake is the cmake config scripts it bundles, then I’m afraid you are very opinionated over irrelevant details that are immaterial to the discussion.
IMO the main task of a build system is to manage inter- and intradependencies to build the software correctly. So, to me, this failure is truly a fundamental strike against it. If I cannot build your software because I cannot sort out your undeclared, unversioned implicit ‘config’ dependencies, I don’t have much use for other features your build system may or may not provide.
That explanation apparently it is: “because not everyone in the ecosystem acts the way CMake developers would want them to”, which I read as “CMakes design for external dependencies is broken.”
Not really, it just means you manifest a blatant and critical misunderstanding of what build systems in general do, and in particular what are CMake’s responsibilities.
As you’re guiding your opinion and expectations on a mental model that’s fundamentally broken while being oblivious to the brokenness of the mental model, instead of identifying the real problem you’re falling back to blame tools.
The issue is quite simple: CMake is a high-level abstractions which is used to generate arbitrary build systems on arbitrary platforms. No platform provides a standard way to make libraries or modules available. At best, Unix-like OSes store include headers and static and shared libraries in specific locations in the file system, but each library can and often does allow users to customize it’s installation. There is no way platform-indendent or platform-specific way to configure these. How do you solve this issue?
CMake solves this issue in a very simple way: library vendors can export CMake config files that downstream dependencies can use to configure their CMake projects. The hard problem becomes a solved problem. However, this requires library vendors to own this leg of the process. Not all projects do. So what can be done about those?
For projects whose managers don’t support CMake, CMake provides two mitigations: it ships with CMake-vended config scripts for popular libraries, and allows users to provide their own custom CMake Find modules.
Describing this approach as “broken” is quite honestly a very ignorant and clueless thing to do.
The fact is, where we are right now, today, the find_package/config script design in CMake frequently makes resolving external dependencies far more painful than it already is without that complication.
This personal assertion makes no sense at all and has no basis in reality. The only thing that find_package does is check if someone made available a script that tells CMake how to configure a dependency. If no script was provided, CMake makes the package as not found. If a script was found but it’s sanity checks fail then CMake marks the package as not found. If a script is found and it’s sanity checks passed, CMake can use the targets in builds. That’s all there is to it. Pain-free.
The is only one caveat: you have to have some idea of what you’re doing, and bad workmen tend to blame their tools.
I 've tried to explain what the problem is with CMake’s design from the view of someone who has to build a lot of other people’s software. Your response just seem to be ad hominem attacks, a reiteration of the underlying problem, and CMake’s broken “solution” to it; I don’t see much respons to my actual points.
I understand that you feel strongly that the issue of how to locate all those many undeclared, unversioned config files - that for all intents and purposes are hidden build dependencies - is outside CMake’s responsibilities. You are allowed that position, but it doesn’t change that locating these dependencies is one of the reasons it is significantly more work to build software that uses CMake than other build systems.
A central point of disagreement, I think, is that CMake is designed around the idea that there is a massive value in the meta-abstraction of the build description into something completely platform and environment-independent. It is possible to question how useful this is in general - however - even from the position that this is a great idea, IMO, in practice, CMake largely fails to deliver this meta-abstraction. The builds still tend to only work on systems and in environments on which they have been tested. And, in that case, what is really the benefit of this meta-abstraction over, e.g., “if win64 then …” “if ubuntu then …”?
CMake is bad and only usable if you write your configuration perfectly. But all the other build systems are worse.
IMO CMake is perfect for large, minimally configurable projects. It handles dependencies for incremental builds well (though maybe that’s more the build system (make, ninja, etc.) than CMake itself), and I personally enjoy using it.
However, once people start getting clever with it, it quickly becomes incomprehensibly complicated.
It is damn hard to write tooling for C++ projects: There are no standard ways to do anything, so you need to add lots of options just to cover a range of project configurations. Those add a lot of complexity very fast, which is unrelated to the actual task you want to solve.
I don’t agree. I find CMake to be hands down the best build system generator out there, and allows the cleanest, most straight-forward projects you can think of.
The problems with CMake generally come from inexperienced developers not doing what they are doing and thinking that CMake is some kind of Basic-type scripting language, and in the process making a big old mess of everything they touch.
As a rule of thumb, if any of your CMakeLists.txt file has an if() in it, odds are you messed up already and should reconsider what you are doing.
I really could not disagree more. CMake is a terribly designed build system.
One of the most crucial things for a build system is to find and link dependency libraries. How does CMake do this?
Each CMake release distributes its own set of library-specific (!) find_package modules. These change between releases, sometimes in backwards breaking ways. Providers of some of these libraries don’t always like how the central CMake find_package module work, the options they provide, etc. and thus provide their own, incompatible, find_package modules. On top of this, some software package systems, e.g. vcpkg ALSO provides competing find_package modules, also incompatible and sometimes with exactly the same name. And, of course, sometimes no such module is available, and the developers of the software using the library provide their own, or just side-step the find_package system .
The end result is a total mess that can sometimes be nearly impossible to untangle without knowing exactly what environment the developers themselves use. Furthermore, you want to build older releases? Forget that, unless you are prepared to hunt down the exact versions of cmake, vcpkg, and libraries that the developers used at that point in time to get hold of the magical combination of find_package modules that actually builds the package. This is work of far higher complexity than matching semantic version strings, which is what we typically do with other build systems.
My impression is that the only people who like CMake are people who use it to build their own software and thus never run into this issue since it obviously works in their own environment that they’ve adapted the CMake build rules to. But, put someone else’s complex multi-dependency CMake project in their hands and ask them to build a 4 year old version and (1) watch them give up and (2) observe them then claim those “other” CMake developers just don’t know what they are doing. (When, in reality, the exact same problem applies also to their own software).
I have many more gripes with CMake than this, but I guess this rant is already long.
This is a gross misunderstanding and misrepresentation of CMake. CMake ships with CMake configure scripts for some popular libraries, but their goal is to lower the barrier to entry to CMale by supportinh projects which haven’t been properly onboarded onto CMake by their maintainers.
Think of it as a the-chicken-and-the-egg problem, but tied to CMake’s adoption. You want to use CMake but you can’t just properly consume a dependency because it doesn’t export it’s own CMake config script, so what do you do? CMake supports those cases by means of user-provided CMake Find modules, which each project has to roll out on their own and ship along with their source code. That sounds like a lot of repetitive work, doesn’t it? So why not provide CMake-provided scripts with the distribution?
If the worst point you can make about CMake is the cmake config scripts it bundles, then I’m afraid you are very opinionated over irrelevant details that are immaterial to the discussion.
You lead with this, but all I see in your reply is an explanation of why it works exactly the way I described. That explanation apparently it is: “because not everyone in the ecosystem acts the way CMake developers would want them to”, which I read as “CMakes design for external dependencies is broken.” The fact is, where we are right now, today, the find_package/config script design in CMake frequently makes resolving external dependencies far more painful than it already is without that complication.
I believe my previous reply gives a very clearly answer: because we get stuck in a situation with multiple competing providers of ‘scripts’ that compete against each other and may apparently all change in backwards breaking ways between releases of CMake, releases of the library in question, and releases of package managers like vcpkg who have taken on themselves to fill in missing scripts.
In the end, it is me who have to dig deep into someone else’s mess of CMakeLists.txt:s to try to unravel the mystery of what feature identifiers they expect from that external depedency and how they expect them to work in the find_package/config script the developers happened to use at the time, so I can then go on an archaeological expedition through all possible providers of that config - including older versions of CMake itself to track it down. Multiply with the number of dependencies - which sometimes is up in the hundreds - and it should be clear why this is a horrible, horrible design of a build system.
If we had to stay with CMake’s broken design for this, at the very least it would need some form of declaration of dependency config scripts with a provider name and a semantic version. That way the problem would at least be solvable.
However, it is more or less only CMake that has this issue in its attempt to be so ‘meta’ about everything. Libraries already come with a build-system independent way to specify features: they are split over several library files and software using that dependency choose which ones to link with - using file names. This works well in nearly every other build system. I don’t get the motivation to try to abstract this into an interface that (for the reasons we’ve discussed above) introduces another intermediate dependency layer.
IMO the main task of a build system is to manage inter- and intradependencies to build the software correctly. So, to me, this failure is truly a fundamental strike against it. If I cannot build your software because I cannot sort out your undeclared, unversioned implicit ‘config’ dependencies, I don’t have much use for other features your build system may or may not provide.
Not really, it just means you manifest a blatant and critical misunderstanding of what build systems in general do, and in particular what are CMake’s responsibilities.
As you’re guiding your opinion and expectations on a mental model that’s fundamentally broken while being oblivious to the brokenness of the mental model, instead of identifying the real problem you’re falling back to blame tools.
The issue is quite simple: CMake is a high-level abstractions which is used to generate arbitrary build systems on arbitrary platforms. No platform provides a standard way to make libraries or modules available. At best, Unix-like OSes store include headers and static and shared libraries in specific locations in the file system, but each library can and often does allow users to customize it’s installation. There is no way platform-indendent or platform-specific way to configure these. How do you solve this issue?
CMake solves this issue in a very simple way: library vendors can export CMake config files that downstream dependencies can use to configure their CMake projects. The hard problem becomes a solved problem. However, this requires library vendors to own this leg of the process. Not all projects do. So what can be done about those?
For projects whose managers don’t support CMake, CMake provides two mitigations: it ships with CMake-vended config scripts for popular libraries, and allows users to provide their own custom CMake Find modules.
Describing this approach as “broken” is quite honestly a very ignorant and clueless thing to do.
This personal assertion makes no sense at all and has no basis in reality. The only thing that
find_package
does is check if someone made available a script that tells CMake how to configure a dependency. If no script was provided, CMake makes the package as not found. If a script was found but it’s sanity checks fail then CMake marks the package as not found. If a script is found and it’s sanity checks passed, CMake can use the targets in builds. That’s all there is to it. Pain-free.The is only one caveat: you have to have some idea of what you’re doing, and bad workmen tend to blame their tools.
I 've tried to explain what the problem is with CMake’s design from the view of someone who has to build a lot of other people’s software. Your response just seem to be ad hominem attacks, a reiteration of the underlying problem, and CMake’s broken “solution” to it; I don’t see much respons to my actual points.
I understand that you feel strongly that the issue of how to locate all those many undeclared, unversioned config files - that for all intents and purposes are hidden build dependencies - is outside CMake’s responsibilities. You are allowed that position, but it doesn’t change that locating these dependencies is one of the reasons it is significantly more work to build software that uses CMake than other build systems.
A central point of disagreement, I think, is that CMake is designed around the idea that there is a massive value in the meta-abstraction of the build description into something completely platform and environment-independent. It is possible to question how useful this is in general - however - even from the position that this is a great idea, IMO, in practice, CMake largely fails to deliver this meta-abstraction. The builds still tend to only work on systems and in environments on which they have been tested. And, in that case, what is really the benefit of this meta-abstraction over, e.g., “if win64 then …” “if ubuntu then …”?
make.include was the perfect build system. Everything went downhill from there.