4 ms·
I've been writing [GNU] Makefiles for years, and have a love-hate relationship with the [GNU Make] tool. I tend to push tooling to the limit, I think it's in pa
by hackrmn 2y ago
I've been writing [GNU] Makefiles for years, and have a love-hate relationship with the [GNU Make] tool. I tend to push tooling to the limit, I think it's in part because I believe in "soundness of scope" -- a tool should have a well defined scope and within that scope "all bases should be covered". In practice that would mean, that with Make I am able to define the dependency graph of pre-requisites and targets (files that Make makes) such that it just about handles the graph resolution complexity for me -- _with variables_, that is.
I love Make because it largely delivers on its promise -- and I am using it almost in _opposite_ to what the author describes. That is, I consider phony targets to be an "illegitimate" feature of Make, and avoid them like the plague. While convenient, targets in Make are heavily geared to be files, certainly most of the machinery in Make was written to assume so, and even the well-known (and documented) targets like "install" and "clean" leave a terrible taste in my mouth as of late, despite these being very conventional.
The problem with phony targets is that they're hard to reason with by Make (unless you actually turn "install" and "clean" into files) and break half of the rest of its assumptions on what targets should be and how they behave. The rest of the problem is the linguistical aspect of it -- if I `make install` am I making an install program/script or what? These kind of vagaries have led me firmly away from ever using phony targets.
As for the rest of it, Make is terribly archaic, but that also lends it strength since the archaic nature is very simple on the flip side.
The "hate" part is me taking a dislike to its bare-bones, in my opinion insufficient variables facility, where only truly global variables (certainly sharing one namespace) exist and juggling these for any non-trivial Makefile is always a problem of its own.
I am no novice with GNU Make, not any longer, but occasionally I need to look back into its manual to remember how e.g. the so-called double-colon rules work (when I suspect I might need one) and the real difference between the `=` and `?=` variable assignment, for when I want to provide default values and so on.
Lately I've been playing with the idea of _implementing_ a [GNU] Make compatible tool just to see if the somewhat _patchy_ scope of [GNU] Make can be given more coverage -- for instance to see if adding locally-scoped variables and more flexible target definition can improve Make? What I mean is to experiment with an implementation that retains fundamental principle and mandate of Make -- dependency graph resolution and reliance on [normally] UNiX shell -- but "upgrading" the syntax and extending the semantics. Because while Make is useful, it's also at times terribly hard to work with. To paraphrase Bjarne Stroustrup (the man behind C++), "inside Make there is a neat sound idea struggling to get out".
- Maken 2y agoHow is your proposal different from CMake?
- hackrmn 2y agoWell, most importantly, CMake can't use Makefiles, and my idea revolves around specifically being compatible with Make in a way where a fork [of mine] would behave equvalently to [GNU] Make for Makefiles which both the fork and [GNU] Make are able to use, while not necessarily the other way around (given how the fork would have features that rely on syntactical constructs [GNU] Make wouldn't want to parse, for example). CMake is a different beast, really. While both have in common that they're build automation tools, you can say, save for some shared ideas, they aren't really that similar once you zoom in past some level of detail. Meaning I hardly can choose to adopt CMake _instead_ of writing a Make-derivative if my goal is to _extend_ [GNU] Make. And I have reasons to prefer Make over CMake, so I am absolutely not interested in extending CMake (or acknowledging it has fit my needs and/or is aligned with the way I like to solve problems I have used [GNU] Make for solving). You could say that CMake is the same as [GNU] Make beyond their different syntax, which is true in a sense, but syntax does decide a lot for each respectively, I would say. The fundamental syntactical differences between the two become larger as one walks the abstraction ladder upwards, and looking at each from the perspective of a user (tasked with, say, building a large C++ program/library), one has to adopt slightly different set of concepts specific to each. To that end, I prefer Make's abstractions over CMake's. Last, the value of my implementing a [GNU, henceforth implied] Make compatible tool, wasn't just for forking an improvement, but also in that when I have written a sufficiently capable fork, say, I can assess _how_ Make was made to work, in my experience one tends to learn a lot about what a piece of software writing a compatible "emulator". I _can_ read Make's source code, but I really don't want to because what I have seen suggests the kind of "organic development" that no longer ideally resembles something an outsider would find easy to grok, even a C expert. It's just the way of those things, unfortunately. Instead, I could pick up Python and write a very bare-bones Make-compatible implementation that would give me a lot of answers for "why does Make work like this?" questions.