There are a bunch of other steps. I have a YAML description of the plugin which I use to generate plugin skeleton, and so on.
Do you really think it makes sense to repeat that again and again?
And then, what if I need to fix a bug in the process?
There are a bunch of other steps. I have a YAML description of the plugin which I use to generate plugin skeleton, and so on.
Do you really think it makes sense to repeat that again and again?
And then, what if I need to fix a bug in the process?
I can see how that can be frustrating. My experience with QEMU's transition to meson is that it was clear from the beginning that Meson would not be able to do everything we needed, but it would let us focus on the few things that are special (e.g. building tests for dozens of targets using dozens of cross compilers) and simplify everything else.
It was not easy: it involved several contributions to meson itself (that everybody now benefits from, for example Postgres), and we do have to live with a couple limitations of the tool, but the outcome was extremely positive:
* 4000 less lines of build system gunk
* A lot fewer requests for help even from experienced developers
* Several new features that required build system hacking could be implemented much more easily
* A transition that did not a require any learning curve for existing developers, because trivial changes to the build system remained trivial
Of course there is still complexity, QEMU still has 11000 lines of build system code. But it was totally worth it.
But, you're describing a 11,000 line program without the most basic abstraction mechanism known to man - user defined functions.
What are the benefits of not having user defined functions? none, zilch, nada.
The Meson team is grasping at straws trying to claim that having user-defined functions would make it Turing-complete. Without recursion, it won't.
Try to imagine how you would structure those 11,000 lines of code if user-defined functions were available? Would you really write it without extensive use of functions?
The only somewhat repetitive idiom is
foo = dependency('name', method: 'pkg-config', required: get_option('opt').disable_auto_if(condition))
to find a library, but otherwise there's very very little repetition of the kind that can be fixed with user-defined functions.
That's not surprising though. Because the user input is very constrained, I find it more natural to think of the build as "first prepare all these things, then use them to prepare all these other things" and so on, not as a complex program. As a very simple example, instead of abstracting "find flags for the C/C++ compiler" in a function and calling it twice, I prepare an array of languages and iterate on it.
It seems limiting but really it's just different and this kind of data flow mindset makes it very clear what is happening when (e.g. what files or arrays are read and which are written), and it also does not need a lot of abstractions (functions).
What it needs is decent data structures, and Meson's DSL is superior to CMake in that respect. So basically the code you write matches the tools you have.
Now I am not saying that Meson is perfect. But the functions thing is kind of a red herring. My #1 complaint is lack of include files (apart from going down to a subdirectory), not functions. #2 is messy handling of targets that generate files in subdirectories and #3 is a silly handling of some options.
The important thing is that none of these things, and not even functions for that matter, are baked in the language. There is room for fixing them in the future, because they are missing features or bugs rather than fundamental mismatches.
To be honest I believe that Meson should have used Starlark as the language, but it did not exist 10 years ago and anyway there is no Python implementation of it.
There is definitely a religious feeling to some of their arguments.
What thing you cannot do? I have used capnproto, generated c++ files and passed the result to the input of targets perfectly ok.
What I want to do is to spend the time and effort in understanding and debugging the process, and then encapsulating it in a single function call. Then completely forget about the details and use that single function call.
What is the advantage in not having user-defined functions? None, zilch, nada.
In many large projects, the build system is necessarily large and complicated. It should be treated like any other code, and would massively benefit from the most basic tool for writing clean, readable code - user-defined functions.
Sadly, as much as Meson is tempting in other respects, without user-defined functions, it is unfit for human consumption.
The excuse Meson uses, that user-defined functions would make it Turing-complete and therefore not statically analyzable is totally false.
Adding user-defined functions without recursion does not make the language Turing-complete. On the contrary, as long as there is no recursion, you can inline the functions and create an equivalent code without functions.
Surely this is hyperbole since some of the largest open source projects consider it a viable build system. Systemd, Mesa, GStreamer, GNOME, Postgres.
Would I be correct in assuming that you are coming at this conversation having never tried Meson?
I tried to port the project I've previously mentioned here from CMake to Meson build, but could not find a way of doing it with Meson build in a satisfactory way.
The lack of user-defined functions is a show stopper because I have a complicated multis-stage build process. It is also very different between platform. Having to repeat parts of this process is disqualifying.
A programming language, and a build language is a programming language, without user-defined functions is unfit for human consumption.
The only thing I would use user-defined functions for is to structure the meson.build file in top-down manner, but each function would be called just once.
The script can examine the dependencies, see if any of them changed and then rebuild only if needed.
It's like Greenspun's tenth law[1]:
> Any sufficiently complicated build script contains an ad hoc, informally-specified, bug-ridden, slow implementation of make.
But then, what do I need Meson for?
Meson provides custom targets, so Meson itself would examine the dependencies and rebuild if needed:
https://mesonbuild.com/Custom-build-targets.html
Obviously this might not do what you need specifically, but it does deal with some of the use cases for user-defined functions.
The entire point of having functions is to capture a "recipe" for building one type of target, and then be able to reuse it again and again.
Many other people like Meson, and many large projects have gone so far as to switch to using Meson. It's great that it worked for them, and it's OK that it didn't work for you.
That's exactly what I've been saying.
> As a result, you really dislike Meson, going so far as to call it "unfit for human consumption".
Meson got so many things right, but this one point makes it unusable for many projects, and prevents it from being industry standard.
Let me say it again, a programming language for writing large scale programs (11,000 lines is a large scale program) without functions/subroutines is unfit for human consumption.
It is made worse by giving a blatantly wrong rationale for not having user-defined function.
The only reason I even bothered spending so much time in responding is the hope that they will reverse this idiotic position.
Clearly there are people who think the position isn’t idiotic, and there are large projects which decided that Meson was a great choice and chose to migrate to it. This means that Meson is fit for human consumption.
A lot of build systems won't support what you're asking here. Most don't do multi-stage builds, most don't have the concept of functions, many rely on external tools (autotools, pkg-config, etc). They're not "unfit for human consumption", which is pretty offensive to the people who chose Meson for their projects as well as Meson developers.
I get that you're offended by the situation. I might even agree with you in principle. But you're all over a Meson release thread, their 1.0 release thread even, shitting all over them, on Christmas Eve! Lighten up!
One thing that I find make CMake configurations completely unreadable is to try to have it do everything, coffee included. Where often a script could be run separately first (e.g. to generate those files), and then CMake could run normally afterwards.
Most people either work on programs small enough where having a conservative approximation of the dependency graph is "fine" and excessive rebuilds are OK, they are just compiling a simple static DAG of known source files, and if they encounter these issues, just structure the project with a little repetition between the between the build time dependencies and the code. Or, they just let the module system in their language of language choice figure it out. These are all completely OK. But it's very expensive to do conservative recompilation and very error prone to do manual repetition in larger projects, and gets very complex in multi-language setups.
You can sometimes structure programs to avoid these sorts of problems with enough foresight, but when you can't, or you need these things, you really need them, and the lack of it will really really hurt.
Where does the presupposition of just some build tools, but not the others (if I am reading it correctly), resulting in excessive rebuilds come from?
Apart from building a dependencies DAG for a project, the next fundamental responsibility of a build tool is to watch for / detect changed graph nodes and apply relevant build rules (expressed as graph edges) to rebuild only the necessary parts of the graph by traversing the path upward (if the apex node is up at the top). Naturally, if it is a dependency at the very bottom of a very leafy graph that has changed and upstream nodes have a dependency on it, it will trigger a large (of a very large) rebuild of the entire dependency path. But that is true of all build systems, not just select ones, and that is what we need buid tools for.
The problem is that many people either can't or don't express dependency rules correctly or dependencies generate build byproducts that are to be dynamically injected into the DAG, and they not properly accounted for in the build rules, and that results in an incorrectly built DAG at the build system run time resulting in excessive rebuilds.
In the ideal world, dependency management is relegated to a module system of a programming language. But since long serving incumbents cough cough C, C++ and Java (not until Java 9 anyway) do not have a module system, people have created a kludge (make) and later multiple kludges (autoconf/automake, cmake, maven/ivy etc) to bandaid the problem.
However a generic build tool can't account for a permutation of all possible scenarios, all possible programming languages, linkers, pre- and post-processors and whatever else needs to be done to produce a build artefact and excel at it in every possible use case. Some do it better than the others in very specific scenarios but do poorly in some other or fringe use cases. Then there are personal preferences. Therefore, the discourse over which build system is better than the other(s) will never end.
The module system is tasked with figuring out dependencies (i.e. files comprising a module), which is true for all programming languages starting from Standard ML through to Rust and anything in between.
Now it really feels that most people complaining about CMake and using Meson instead are not in this situation. And if they were, it would seem more pragmatic to me to say "CMake is fine for small projects, but at some size we needed something else" than "Meson is obviously, definitely orders of magnitude better because it took me less time learning how to compile my 20-files project".
And this isn't something that ultra-massive billion line projects singularly suffer from. You can easily suffer from all these issues and more in small and big software projects alike. I've suffered from these problems plenty in FOSS and commercial software that were small and large. "Computer program that generates another computer program" is actually a pretty normal and fine way to build software. But when compilation is expensive, these things matter, and you can't keep making excuses about it.
A build system should accurately track dependencies and rebuild things. It's that simple. You can say that running two commands is OK or whatever, but don't try to imply that people who want that criteria -- accurate deps and rebuilding -- aren't trying to solve problems they actually have, and then ditch the example when it becomes inconvenient to you that they are.
I don't try to have cmake do too much, e.g. I don't run the generation (or python scripts or custom functions) from cmake, because it quickly becomes a mess and running the steps manually / in a script is usually easy.
I haven't seen a project yet where the cmake config is not a mess (because let's be honest, most CMakeLists out there are pretty bad) and where this is clearly a limitation of cmake (and not of meson).
Happy to get an actual open source example where that's clearly the case, though.
I don't get it. Also shell scripts are pretty good at running a couple commands, if really that's important.
I actually have maintained FLOSS projects, and in my experience, throwing everything in one cmake project makes it harder for contributors to understand. Because they only ever use one command, and the day something fails they have to dig into it to understand what that custom command does. On the contrary, if they need to run the generation script first, then they know that there is a generation step involved, and suddenly the whole thing feels less like magic.
So that is my opinion: if your FLOSS project uses a big custom build system (because you customized your cmake to do all sorts of custom things), then probably I will not contribute. Because I have other things to do than learning your custom integration of standards steps I actually would already know.
What's better is what is easier to understand and maintain. If your whole project is made of one file and builds with one simple call to a compiler, IMO it's fine to just have this line in the README, no need for a custom_target in cmake.
Now the thing is that often, I see that people put a lot of importance into the "it should be easy to get started for beginners who don't know the tools", and everything is glued together using some custom functions. The goal being that a beginner can essentially run "make" (yep, I've seen Makefiles wrapping CMakeLists for that) and the project will build (sometimes it will even install dependencies on the system, that's crazy). But then if the beginner wants to understand what's happening under the hood, well... it's a big pile of custom glue.
Now if, instead of that, you document the tools you are using in your README, and use the tools in a standard way, then people who know the tools will get started quickly ("oh, it's protoc then cmake, I know exactly how to use that"). And yeah, beginners will need to learn how to run those tools. But that's fine IMO, because if you use the tools the standard way (without custom functions), then the beginner can just look for the official docs.
Standard is better than custom.
No, it cannot be done before CMake runs. It is a target like any other build target. It depends on other files or targets, and should be rerun if any of them change, and if it is rebuilt, there are other targets that depend on it and should be rebuilt, and so on.
The entire point of the build system is to concisely and accurate specify the build graph, and only rebuild what is necessary.
For the big majority of projects out there, it seems to me that it is perfectly fine to handle that manually, because projects are not that big. Do we agree on that?
Then of course, if you are a GAFAM with a huge monorepo, probably you need more advanced tools (they don't build their own tools for nothing). My feeling, though, is that most people complaining that CMake is crap actually work on small projects and can't be bothered to run protoc manually "because their philosophy is that it should all work with one button".
Now it's not a pain for me, and apparently you're on the side of those who find cmake painful. Not sure how unreasonable my way is :)
Let's also agree that every time you change a .cpp file you compile it, and every time you change a .h file you compile all the affected .cpp files.
No we don't agree on that. That is the entire point of having a build system.
Even proto files you gave as an example, may import other proto files. When you change one of the imported files you have to recompile all proto files that import that file, transitively.
I have, unfortunately, worked with build systems that do not capture all dependencies and systems that capture way too many dependencies. The former resulted in many hours spent debugging non existent problems, and the latter resulted in many hours spent waiting for compilation to finish.
and to me, build procedures that aren't completely implemented in cmake but require to install who knows what shell / platform / language / etc... in addition are the bane of my life as they make everything harder and more brittle and less cross-platform.