I have stopped using MSVC many years ago, so I do not know how it handles dependencies, but with gcc or clang that works regardless of platform, with compiler-specific options.
Moreover, software for one platform can be built on another platform. What matters is only the platform used for building, where the compilers are hosted, not the target platform for the built software.
In general, I extract all platform specific definitions, like the names of the compilers and their command-line options in platform-specific Makefiles, which include a generic Makefile with platform-independent definitions, rules and make targets. Any platform-specific details, like SDK paths, are encapsulated in the definitions of certain "make" variables.
While I have not needed to update the platform-independent generic Makefile for decades, the platform-dependent sections need updates from time to time, but that is not done for every software project, but perhaps once per year or more seldom, when significant new compiler versions, standard libraries or other new software tools become available.
I have not seen yet any reason for using Ninja. Perhaps it may be faster when building something like chrome, but because chrome is likely to be the slowest-compiling software project known to mankind, it is hard to tell how much is gained by Ninja. For software projects of more typical sizes I have not seen noticeable advantages of Ninja. Traditional make can keep all the cores of a CPU 100% busy, so there is no way any other building system can be appreciably faster. There are some very bad Makefiles that are intrinsically slow, but that is a problem of those Makefiles, not of "make" in general.
Using a minimum project-specific Makefile section can only simplify debugging, not making it difficult.
I have never seen any evidence for the claim that cmake scales better. On the contrary, at least in all the open-source software projects that I have ever seen cmake is a major source of bugs in the building process that nobody knows how to fix. (i.e. after some software updates cmake fails to find files that exist, presumably due to some errors in the cmakelists that appear to be arcane enough that they are hard to find) I have never seen in projects that use traditional make such errors that are so frequent in all the projects that use cmake.
I have a lot of experience in building software projects, because for many decades, with the exception of a few professional programs without alternatives, I have been using only programs that I compile from sources. Most projects have very bad Makefiles, which are very hard to maintain, i.e. to change when files are added, deleted, moved or renamed, but at least they build the software projects reliably. Whenever cmake is used, building a new version is always an adventure with unpredictable outcome.
And make tends to fall significantly behind Ninja in my experience for cold builds. Here’s a post from someone else that echoes my experience https://david.rothlis.net/ninja-benchmark/ but for a sufficiently complex project even a well tuned make build is about 20% slower on CI, which is about 10-20m per build.
It also seems like you’re giving make the benefit of the doubt when it comes to “most being badly written but my own is great”, while simultaneously deriding cmake without giving it the same benefit.
But to your larger point, and going back to my C vs other languages comparison (let’s pick C++ for arguments sake). Yes Make can do it all, but even by your own statements it requires doing everything correctly and there’s significant room for error. CMake makes the trade off of abstraction for a more consistent experience with minimal work.
This is similar to C and C++, where yes C can (1) do much better in the very very specific right hands, but (2) it can also become an unwieldy mess. C++ with RAII may seem perplexing from the lens of only accepting the former and not the latter. It lets people spend their time elsewhere on the system.
So reading all the makefiles should take a negligible time, even in a big project.
I have seen too many projects with huge makefiles and with much more makefiles than necessary, but all of those are examples of misuse of make, which are easier solved by an efficient use of make, instead of by replacing make with another tool.
For instance, there are many projects with a Makefile in each directory with source files, which is a very bad choice that multiplies the number of makefiles. Makefiles should exist only in a dedicated build directory, not in source directories, except for very small projects with a handful of source files.
The best way IMO, is to have a single very small Makefile for each final file that must be built, e.g. executable file or library, in a dedicated subdirectory of the build directory. This allows for a maximum simplification of the Makefiles and their number and size is independent on the number of source directories and on the number of source files.
It's also doing a lot of variable expansion and if the makefile uses macros like define....endef to generate rules it has to expand those.
Make is also often trying all sorts of pattern rules to see if they will allow it to fulfill dependencies. This takes time.
If makefiles are small and you run make on each one separately then you miss dependencies across modules and you get the syndrome where program A doesn't get rebuild when library B changes.
So there's a price to pay for everything. Ninja is a lot faster because it just doesn't have some of these features and you can make a big makefile where dependencies work properly with it and not pay the price of slow parsing.
I worked on builds that took 12 hours on a large cluster of build machines where parsing was 45 minutes - so it matters. The more general solution would be to use a packaging system like the Linux distributions but even that doesn't save you from every problem as I also found out when working with OBS on a linux phone distribution.
However, the same link shows that even for relatively large projects the time difference is less than a second, which matches my experience.
Ninja requires much more effort for using, as it is not a complete build system, but just the execution component, and except for projects of chrome size that effort is not worthwhile for a subsecond gain in certain scenarios.
I cannot imagine how a "tuned" make build can be 20% slower, except when the project is not really tuned, but it is badly organized. A "tuned" Makefile means that the execution time for "make" is negligible in comparison with the execution times of the compilers and linkers. The time spent in "make" should be measured in seconds an most, never in minutes.
Only a very slow file system can cause "make" to be much slower than Ninja, because the dependencies for "make" are stored in many ".d" files, equal in number with the source files. On modern SSDs or RAM disks, that is never a problem.
Perhaps you are right about CMake. I cannot be certain whether CMake is good or bad, because I have never created a CMake project myself, I have only built CMake projects created by others. Nevertheless, there certainly is a problem with the CMake users that I have not seen yet one that can explain which are the advantages of CMake. All the tutorials that I have ever seen about CMake were showing how to do in a complex way things that can be done in a simple way with GNU make, so I never had any reason to investigate any further.
What I know for sure is that much fewer understand how to use CMake than how to use make, because the frequency of bugs in CMake projects is much greater.
I would like to see how CMake can provide minimal work. With GNU make, creating a new very simple project needs only copying a template Makefile in the source directory. For a more typical software project, the template Makefile must be edited to add a list of directories, those that must be searched for source files (I actually write the list as a prefix directory plus a list of subdirectories for it, as that is how I normally structure the projects).
For external dependencies, up to 3 lists may be added, of libraries, include directories and perhaps of library directories, which should suffice.
That is all. Nothing more. I normally build each target file of a project, e.g. executable or library, in a separate subdirectory of the build directory, so by default it is not necessary to write it in the Makefile, because the name of the subdirectory will be used for the target. The root of the build directory contains a Makefile that I never change and which descends in each subdirectory to build its target.
How can be CMake more simple to use than this? All CMake projects that I have ever seen were much more complicated and they were very difficult to modify, with a very large number of project-specific details that have no place in the project configuration files, because the building system should be able to deduce them.
All the examples of using CMake that I have ever seen have demonstrated a tool with a much lower level of abstraction than GNU make and which is tedious to use.
GNU make does not need any such thing like the CMakeLists that are ubiquitous with CMake.
Then what is a Makefile?
And if your dependencies only amount to looking for source files, then that’s fine. Many are more complex than that and include configuration options themselves.
Again, I think you’re hyper indexing on just your own setup and ignoring anything outside of that, while simultaneously hand waving the number of bad makefiles out there and any shortcomings to drive home that Make is simplest.
Meanwhile by your posts, you’ve had to create an entire scaffolding of support around your makefiles to make it appear that simple. How is that any different than CMake or any other build system/generator? In the end you’ve just orchestrated your own version of such a thing and are decrying any other system as overly complex or just needing adjustments.
I believe we’re just talking past each other, but I’d really encourage you to consider that the reason you’re confused why people don’t just use Make is perhaps well founded and rooted in the number of things you have outright dismissed or don’t care about for your own projects. I’m not even specifically advocating for Cmake, but there’s no great mystery why so many projects use it or other build systems; it mostly lets them shift the responsibility of configuration to the system and focus on their task at hand. Again, exactly the same as C vs C++/Rust/etc…
CMake script syntax is not very great, and debugging is no better than sprinkling printf's throughout... So, why not use Autotools? /bin/sh has been the norm for most build processes, to the point where the later-designed YaML syntax is _effectively_ the SAME as a Makefile with /bin/sh statements running the pipeline procedures.
CMake came along and tried to fix complexity in Autotools while adding kitchen-sink baggage along the way.
I was merely pushing back on the person perplexing on why people don’t just use Make.
meson lags behind for supporting new features like different languages (Swift, ObjC etc), Xcode/msvc updates, IDE integration (CLion is amazing), build outputs like frameworks.
basically, Mesons better ergonomics lose out to practicality for my use cases. To get it do what I need, I’d be making my own meta system around it.
While there is certainly value in using CMake (as so many projects have chosen it as their build system)—it also becomes a sort of cautionary tale whence projects try to reinvent the wheel to fix some deficiency, only to build a complex system with it's own set of deficiencies. Obviously, part of this is due to the fact that commercial entities (cough Microsoft) deviated from any sort of standards-based approach to building software.
But for every build-configuration system, Autotools seems to be the only one which dictates that a developer machine merely have POSIX tools installed. Nowadays, that's all the big players, considering that MSYS2 on Windows is better than ever. And it supports many languages to boot: C/C++, Objective C/C++, Go, Fortran, Erlang.
How does one discover dependencies in a cross platform way with Make without writing the logic themselves to stay up to date with platform changes? Or picking up configuration options from dependencies.
How does one make use of Ninja with make? Or discover changes to sdk paths for new platforms?
You end up having to duplicate that logic across every repo that needs it. Along the way you accrue lots of variance.
In the end you get something that is difficult to debug and scale. That’s why so many projects moved to cmake. It scales better. In much the same way that you can technically do anything you need with C, but other languages add ergonomics and scalability that people value more.