15 ms·
An opinionated approach to GNU Make
- blibble 4y agothe most common problem in pretty much every Makefile I've ever seen is not specifying the dependencies correctly like nested header files, or forgetting to update them when the code is changed (so everyone runs make clean all instead every time...)
- IncRnd 4y agoMany people forget to include the makedep utility in their makefile. Makedep creates additinal dependency targets, so that source files depend upon the header files they include.
- leononame 4y agoI mostly use make like a package manager agnostic version of the "scripts" section in package.json, and some of the recommendations here are like a revelation for me. I didn't know you could change the tab character to a > for the commands, that's such a great improvement imo.
- KindOne 4y agoPrevious discussion (2019) with 194 comments. https://news.ycombinator.com/item?id=21812656 https://news.ycombinator.com/item?id=21812656
- jchw 4y agoThis seems like a lot of work to not just consider ninja, meson, CMake, etc. I fully understand that the simplicity and portability of Make is alluring, but if you are actually using it to build C software it is a catastrophically poor choice and you can spend a ton of time and effort trying to come close to what you can get out of the box on a modern build system. If the tradeoff was that Make was easier to use and debug, then maybe it could be justified, but in general my experience is that it's worse. There are probably some use cases for Make where it remains difficult to replace for one reason or another, but most people using it anymore are not in that position. Now, it's usually more work to keep using it.
- pfranz 4y agoOut of the ones you list CMake is the only one I've authored and maintained and I still end up having to understand and troubleshoot Makefiles. Depending on the size of the project and the needs I try and use the fewest number of abstractions since I end up jumping down to troubleshoot anyway. For toy stuff I'll just compile on the commandline (maybe write a bash script). I'll write a Makefile if I need to start wrangling too many things. CMake usually comes in if I need to go cross-platform or incorporate another build system or dependency that needs it. I think most places probably need CMake, but quite a few don't. If Make, as is, works then it makes sense to come up with opinions and standards that streamline authoring and maintenance.
- bin_bash 4y agoI thought most people use ninja through CMake? Do people actually write ninja scripts directly?
- benreesman 4y agoI’d love to go on an anti CMake rant, because I hate it, but @vzverovitch (fmtlib author) is the undisputed God of CMake trolling: https://mobile.twitter.com/vzverovich/status/1380648272503533568 https://mobile.twitter.com/vzverovich/status/138064827250353...
- jchw 4y agoThe point isn't that CMake is good. I don't think anyone is arguing that CMake isn't an ugly, weird mess. I can point out several annoyances that are frankly disturbingly stupid. However, it also offers practical answers to real problems that even Autoconf don't do a good job with, and that makes it valuable. CMake can handle out of tree builds, vendoring, cross-compilation, packaging RPMs/debs/even DMGs, library and platform detection, test suites, abstracting build systems, MSVC/Windows... If you are going to tell me about the "best" way to compile software and the answer to this is either "shrug" or "Here's a shitty 1000 line Bash script you can copy" then I'm going to continue to discard the advice, because frankly it sucks. CMake, also, for all its faults, has improved a fair bit from the 2.x days. I'm not saying it will ever not be ugly, at least as far as the language itself goes, but they're definitely cleaning up a lot of the worst mistakes over time and it's making CMake a lot less of a bad proposition. You do have to opt-in to some of the new practices, but it is nonetheless worthwhile improvements.
- kennu 4y ago> .RECIPEPREFIX = > That seems like a terrible idea. You change the basic syntax of the entire Makefile, forcing anybody reading it to get used to your custom indentation, where almost every line starts with an unnecessary >.
- yjftsjthsd-h 4y agoAlso, the justification seems to be > And you will never again pull your hair out because some editor swapped a tab for four spaces and made Make do insane things. Which... I guess that would be annoying, but maybe fix your horribly broken editor rather than mutilating the Makefile?
- Spivak 4y agoAre you going to fix everyone else’s editor too? Software is a multiplayer game and eliminating a whole class of error that hinges on someone not noticing the difference between invisible characters in a diff is a huge win.
- eropple 4y agoJust about everyone I know uses VSCode, which handles Makefiles correctly out of the box, or I can trust to have a correct vim config, so honestly I think I'm okay with it. If something breaks, it'll break loud. There's a lot of other really good advice in this article but this one feels unfounded.
- dllthomas 4y ago> I can trust to have a correct vim config And this isn't some obscure thing that people need to have got around to configuring; vim works properly with Makefiles (marking non-tab leading whitespace as an error) out of the box on at least Ubuntu and I'd guess much more widely than that (all distros and Mac and Windows and also when building from source wouldn't surprise me, although it also wouldn't surprise me if there were a couple of exceptions where things would need to be massaged).
- blueflow 4y agoThis results in a pretty un-portable Makefile. Portability is a desirable feature for a build system, which is supposed to help other people on other systems to build your software.
- yjftsjthsd-h 4y agoGiven the explicit choice to use GNUisms and promptly overriding the SHELL to explicitly use bash, I'm quite confident that this author is not concerned with portability concerns.
- omginternets 4y agoQuite, but then the posture of "you're doing it wrong" is utterly incomprehensible. It's clear that the author is making rather strong assumptions about the runtime environment.
- blueflow 4y agoFor the purpose of self-promotion, i guess. Being confrontational brings attention.
- brabel 4y agoWhat do you mean by portability? Are you able to confidently write a Makefile that works on any Linux distro, BSDs, MacOS and Windows? Or for you, portability means Linux systems only? Honestly curious, because I've gone to the trouble of writing my own build system just so I can use the same build file on any OS whatsoever (which to me, means using a cross platform language for everything, not relying on bash or any other shell).
- deleted 4y ago[deleted]
- coliveira 4y agoThis is bad advice. Changing the default prefix for recipes is worse than using tabs, whatever your feelings about tabs are. Just use make as it was designed, it will work better this way.
- cpuguy83 4y agoHow about "your docker builds are wrong", too. Don't generate some random id and a tag (as in the post). Use docker's "-iidfile" flag when building to write the actual id of the image to a file which can then be used in a "docker run". Likewise, you can use "--cidfile" in a "docker run" to output the id to a file and use that later for accessing it.
- bin_bash 4y agoCan you explain this a bit further? I don't think I understand the point, but I've been using docker more lately and this sounds like it could possibly be something I could use. It sounds like I could do something like: docker build --iidfile .dockerid && \ docker run -it /bin/bash --cidfile .dockerid Without needing to copy and paste the image ID? Is that right? Why wouldn't I just tag the image? docker build -t myimage && \ docker run -it myimage /bin/bash
- cpuguy83 4y agoNot quite. --iidfile writes the image id of the build to a file. --cidfile writes the container id of the container to a file. To use the output of --iidfile in a "docker run", instead of specifying an image name call "$(cat <iidfile>)". As an example: docker build --iidfile out/imageid ... docker run ... $(cat out/imageid) --- edit for formatting ---
- andrewmcwatters 4y agoI hate developers like this. This is the attitude of every weirdo developer I've had to work with who thought they were brilliant instead of just using the stupid tool as it was intended.
- dataflow 4y agoHonestly his takes (mostly) aren't wrong or niche (at least not all of them; a few like the tabs thing are debatable). Some of these you only realize once you've wrestled with Make for a while and spent time thinking about the underlying problem, which many don't do. Knowing some of these ahead of time can save quite a bit of a headache. They're worth considering even if you don't follow all of them.
- jart 4y agoIt would have been classier if they'd replaced the tab character with an emoji. That's what someone on Lobsters recommended, once I told them about .RECIPEPREFIX.
- cpuguy83 4y agoThat's a strange take on the post. The post only uses built-in functionality. Options exist for a reason, and defaults are _really_ difficult to change in software used by millions of people.
- blueflow 4y agoThe effect is that the Makefile now has a dependency on bash and GNU make. What happens to the *BSD or non-GNU linux users?
- formerly_proven 4y agoThey usually don't matter (and unseen-untested portability is simply wishful thinking), but particularly don't matter for projects you're responsible for and decided they don't matter. That doesn't mean you must randomly preempt portability, of course.
- throwaway9870 4y ago
- linkdd 4y agoThere are a million other ways to express your opinion without ad hominem insults. Learn how to communicate and chill, no one forced you to read the blog post, and you could just have ignored it.
- throwaway9870 4y agoWhen you come at people with the attitude the author has, you should expect it right back at you. That is why anyone who knows how to give a proper talk or write a professional paper does not do that. Leave it to popular culture sites to treat people like that, it should have no place in professional presentations.
- linkdd 4y ago> When you come at people with the attitude the author has, you should expect it right back at you. The attitude being: a clickbait title immediately dismissed as "an opinion" --> An opinionated approach to writing (GNU) Makefiles that I learned [..] Use the above as guidelines, not dogma But maybe you haven't read the article and was just offended by the title. Still not a good reason to insult people like you did. The author of the article never insulted anyone. > it should have no place in professional presentations Good thing then that this article is posted on a personal website. It's not even the author of the article who posted the link on HN. Still not an excuse to insult people.
- bee_rider 4y agoI like parts of it! The tabs vs spaces thing seems pretty silly to me. If your editor is randomly swapping tabs and spaces, get a better editor. Tab is the default in a makefile and that seems fine. The suggestion to use "> " instead of tab just looks noisier. The observation about the filesystem is good and hopefully well known. The way to think about makefiles is as a tool for creating files (it is very oriented toward this), not as a general purpose scripting language (it is just a worse version of whatever scripting language you are in it, if you use it this way). I do wonder if he could structure his tests to have them actually generate output files which make could track, and also have his tests track dependencies. Point taken about the magic variables. Sometimes they can get obscure (although they are pretty easy to look up). IMO one he's missing, though, is the pattern matching % operator. If make isn't generating at least some of your recipes for you, then why not just make a "build.sh" script?
- yjftsjthsd-h 4y ago> Point taken about the magic variables. Sometimes they can get obscure (although they are pretty easy to look up). IMO one he's missing, though, is the pattern matching % operator. If make isn't generating at least some of your recipes for you, then why not just make a "build.sh" script? Probably a preference for explicit over implicit, just like setting `MAKEFLAGS += --no-builtin-rules`. Of course, promptly using a couple of magic variables because they're "common enough that you quickly learn to recognize what they mean" is... a bit amusing, to me.
- dllthomas 4y agoAnd on the topic of "get a better editor", if you (quite reasonably) prefer something more visible than a tab... you can do something to make tabs more visible in your editor. That way it'll work for any Makefile you open, not just those you're responsible for.
- zelphirkalt 4y ago> [...] it is just a worse version of whatever scripting language you are in it, if you use it this way [...] If I may quote this little part: Oh no, it is actually much better, than what a huge part of developers in web development do: They use package.json of their project, where they add under the "scripts" attribute calls to commands, which contain again calls of "npm run", which again reference other "scripts" ... Of course there is no way to actually specify dependencies (previous steps a step depends on) as actual dependencies and the whole thing becomes a procedural thing, instead of a more declarative thing. There is also no good way other than writing whole shell conditions in there, in a JSON file inside a mere string, if the command relies on a file existing. So in effect the logic will always run the step, which creates that file. So this considered, I don't think Makefiles are really doing badly. You can do much worse. EDIT: > If make isn't generating at least some of your recipes for you, then why not just make a "build.sh" script? Because then you don't get what Make brings to the table: tab completion, declarative dependency specification between steps, declarative definition of targets. You would have to write code for these things yourself in that "build.sh" script.
- yjftsjthsd-h 4y agoTitle: "Your Makefiles are wrong" Content: A lot of subjective preferences, with the only thing people are probably doing "wrong" being not properly mapping files as inputs and outputs (which could be a correctness problem but is probably either a mere inefficiency or complete non-issue). If this had been titled, say, "An opinionated approach to writing Makefiles", or perhaps "How to use GNU Make in a completely unorthodox way that I really like", I wouldn't mind it so much.
- ec965 4y agoThe purpose of a title is both to summarize content and grab the readers attention. It's up the author which one they put for emphasis on. You clicked so it worked, even if you don't like it.
- yjftsjthsd-h 4y agoClickbait working isn't really a great defense.
- flykespice 4y agoThe user only saw the headline and closed, isn't the author aiming visitors to read the content or clicks? The same analogy could be made to a baker luring customers in their bakery with an attractive facade but the very same "customers" just give a quick glimpse and leave. What is the win?
- yjftsjthsd-h 4y agoIn fairness, I did in fact read more or less the whole thing. I walked away with a fairly low opinion of the article and the author, but I did read it :)
- chrismorgan 4y ago> Make leans heavily on the shell, and in the shell spaces matter. Hence, the default behavior in Make of using tabs forces you to mix tabs and spaces, and that leads to readability issues. I have written a great many makefiles, simple and complex. I can’t recall a single time I’ve needed to mix tabs and spaces in one (though I have had to mix them multiple times in both YAML and HTML). (As for anything like accidental mixing, for my part I have a sanely-configured text editor and so don’t need to worry about anything silly like tabs being turned into spaces. Tabs are superior to spaces anyway. ⸺But I do use spaces for Rust and Python where that is customary, I’m not completely antisocial.) > .ONESHELL ensures each Make recipe is ran as one single shell session, rather than one new shell per line. This both - in my opinion - is more intuitive, and it lets you do things like loops, variable assignments and so on in bash. .ONESHELL also means that your makefile will behave differently from how anyone that’s familiar with makefiles will expect it to. But I guess this does explain why you went enabling strict mode, since you’ve basically turned off the near-equivalent default functionality from Make. Note also that you can do loops and such already—you just need to use line continuations (put backslashes at the end of each line, which Make will consume). Yeah, the default behaviour is idiosyncratic and will lead to surprises in the unwary (though they’ll normally observe it immediately, when the cd is ineffective on the next line, or when the if/for causes a syntax error), but I think Make has generally become niche enough that I’d prefer to pander to people that know Make than normal people. :-) > .DELETE_ON_ERROR Two-edged sword: it also means you can’t inspect what went wrong by looking at the file. You’re also making the very dubious assumption that merely deleting this one file will fix everything. A few times when I’ve known something to be fallible but want to be able to inspect what it created, I’ve put in something like a `… || { touch --date=@0 $@; exit 1; }` suffix so it still fails, but first zeroes its mtime so that subsequent runs will see that it’s out of date, though the file still exists. I’m not saying it’s wrong or a bad idea, just that it’s worth considering the implications fully rather than blindly applying it.
- eqvinox 4y agoWow. This is atrocious. > You really just need the .RECIPEPREFIX = > Now I can't copy & paste a block anymore (into the shell, to run it), and all my editor indentation settings are broken. > SHELL := bash And the Makefile is now non-portable. > .SHELLFLAGS := -eu -o pipefail -c If this matters, it's likely you're wedging too complicated things into one recipe. But less bad than the other suggestions. > .ONESHELL Funnily enough using this is the primary reason the previous item becomes important. The subtly changed behavior also turns multi-line recipes into a giant footgun if you end up with a non-GNU make. (skipping a few that are not as bad) > out/image-id: $(shell find src -type f) Might be OK in a single rule. Otherwise, it's calling find more... and more... > Sentinel files Actual good practice to end it on.
- jen20 4y ago> And the Makefile is now non-portable. The article calls out GNU Make, so almost everything else in there is also non-portable.
- eropple 4y agoEven beyond that I don't really understand the criticism. Like, I don't remember the last machine I had to do serious work on that didn't come with GNU Make either out of the box or added as part of some very basic bootstrapping scripts. (I don't know what a Mac comes with because my bootstrap for every new machine has coreutils in it and every project has a Brewfile with it too.) I definitely don't remember the last machine I saw that didn't have bash on it.
- dllthomas 4y agoAnd in this case we're concerned about computers that do have GNU make but don't have bash...
- eqvinox 4y agoIf you write Makefiles for your own personal use — sure. If you're working in a team with other people, or are publishing things for a broader public to use… no. Especially since things like .ONESHELL don't just flat-out cause errors, but rather introduce subtle and insidious distinctions in behavior.
- nrclark 4y agoThis article has some questionable advice imo. SHELL := bash Bash is a much slower shell than Dash, which is why Debian and friends don't use it as /bin/sh. .ONESHELL mitigates the speed problem, but you could also just use the default shell and leave ONESHELL turned off. Use bash strict mode .... .SHELLFLAGS := -eu -o pipefail -c I wish people would stop cargo-culting the so-called "strict mode". The -e flag is only useful because the author likes .ONESHELL mode. If you leave ONESHELL turned off, then you don't need it. The -u flag is useful sometimes, depending on coding style. I use it on complex scripts. Individual Makefile recipes maybe don't want that much complexity though. Also the -u flag makes the shell's variable-handling behavior inconsistent with Make's. The pipefail option is Bash-specific, and only works because the author likes to set SHELL to Bash in their Makefiles. It's also not a good default in my opinion. There are times when it's useful, and other times when it's the opposite of what you want. Just depends on the pipeline that you're writing.
- jart 4y agoThe fastest shell is to not use shell special characters. For example, if you say `foo bar >/dev/null` then Make needs to launch your program as `sh -c 'foo bar >/dev/null`. But if you say just `foo bar` then Make can pass that directly to execve(), bypassing the shell entirely. Sometimes I actually do this: SHELL := /bin/false Just to make sure my Makefile doesn't use shell syntax. If you want a `.STRICT` mode, then try Landlock Make.
- jwilk 4y agoDoesn't work for me: $ make -v | head -n1 GNU Make 4.3 $ printf 'SHELL := /bin/false\nall:\n\tls\n' > Makefile $ make ls make: *** [Makefile:3: all] Error 1
- jart 4y agoI completely forgot that the GNU Make source code has a check to see if the shell is bourne-compatible (it just strcmp's with sh, bash, and a hard coded list), and then only applies that optimization if it is. I deleted that code in my Landlock Make fork so I could use shells like /bin/false and still get the optimization. So sorry about that! Give it a try with Landlock Make https://github.com/jart/landlock-make https://github.com/jart/landlock-make and https://justine.lol/make/ https://justine.lol/make/
- zzbn00 4y agoA little trick to rebuild targets based on the build options: use .VARIABLES and pipe to sha256sum to create an option-dependent suffix for all built files: https://bnikolic.co.uk/blog/sh/make/unix/2021/07/08/makefile https://bnikolic.co.uk/blog/sh/make/unix/2021/07/08/makefile In this way a meaningful change to the makefile triggers rebuild automatically like it should
- bin_bash 4y agoI think the `.RECIPEPREFIX = >` bit triggered a lot of people here in the comments, and I agree. That would make drafting newlines a huge pain in any editor. Just enable "Show Whitespace" in your editor if you want this. That said, I'm more concerned about the guidance to not use .PHONY and instead do this: # Tests - re-ran if any file under src has been changed since tmp/.tests-passed.sentinel was last touched tmp/.tests-passed.sentinel: $(shell find src -type f) > mkdir -p $(@D) > node run test > touch $@ The author is right, that does use make in a more more idiomatic way by relying on a real file, but I see 2 major problems: * That's a lot of logic for something that should just be super simple. * When I say `make test` I want it to run the tests. I don't care if they've passed before and the files haven't changed. Really though, make just isn't a great tool for build scripts. The syntax is horrific and it's hard to scale it into something readable. If I started a new project I'd probably consider Just: https://github.com/casey/just https://github.com/casey/just (though I haven't had a chance to use it myself yet).
- brabel 4y ago> I don't care if they've passed before and the files haven't changed. This is a common interjection from some people... which means you think that your tests are not deterministic, otherwise it would be completely pointless to run them again without inputs changing. I actually admit that from end-to-end tests, this is usually true despite our best efforts to the contrary... but for unit tests, I really think tests should be 100% deterministic and only ever run when something they rely on changes. The Unison programming language goes even further[1] and it NEVER executes a test again once the code under test has been "committed" into its image. [1] https://www.unison-lang.org/ https://www.unison-lang.org/
- bin_bash 4y ago> completely pointless to run them again without inputs changing What if I installed some dependency outside of my project? What if I'm trying to test performance with `time make test`? What if it failed due to a transitory error—or I'm simply trying to discover if it is transitory? What if I made a change to the way the tests are run in the Makefile or a different file not in ./src? Like running `npm install`? What if I want to set an environment variable or pass an argument to the test runner?
- jp57 4y agoThe HN and the blogosphere generally are replete with unconvincing "you're doing it wrong" posts. This is one. I use make (and have used it off and on for a long long time: since the late 80s). I don't do any of these things. If the author is going to make a convincing case his way is "right" and other ways are "wrong", it's incumbent upon him to clearly state what failures I will avoid. Then I can evaluate how often I encounter them, and decide how important this advice is. As stated, it doesn't seem very important. I frequently run into similar situations with more junior engineers at work. One will insist on dogmatically adopting some "best" practice advocated somewhere, and when I ask what failures or bad situations we'll avoid, or what good situations we'll encourage, they can't answer. In their minds, someone (outside our team or the company) said it's better and so it must be. I think it's important to avoid invoking incantations, and to understand the reasons for each choice you make. In this article, I don't see that.
- _0w8t 4y agoThe article does state the reasons for the rules and gives examples how not following them may lead to troubles.
- avg_dev 4y agoI feel the same way. I strongly believe that choice of language makes a huge difference in your material’s reception. Any time I am told that something I’m doing is wrong by an article, an inanimate piece of text that clearly has no cognition thus no idea what I’m doing or not doing, I think that the person who wrote it, is, in fact, communicating wrong. I was on the fence about posting this reply; after all the guidelines tell us to stay relevant to the material, but I do believe you have done that.
- pessimizer 4y ago> I strongly believe that choice of language makes a huge difference in your material’s reception. Not with me, unless you're talking about the difference between clear and unclear language. I'm not bothered by language that some people seem to class as patronizing or like you know everything or whatever. When somebody is explaining something to me, I don't want them to explain it to me like I know it already. I want them to explain it to me like someone I've hired to explain things to me i.e. like they're the expert and I'm not. If I think I know everything about make, there's no reason for me to have clicked on this other than an urge to seek out things that confirm my sense of self-worth, or to get upset about things that threaten it.
- bigcat12345678 4y agoLooking at bazel's complexity, and its popularity, one should see that Makefile and its dependents, which forces upon writer a centralized model (where one Makefile dictates how to build all code files scattered in subdirectories), cute but unreliable grammar (tab vs. space), unnecessarily convenience features (for god's sake, I never managed to learn any thing beyond simple target & deps, and things like PHONY target are just beyond my mental capacity).
- mkl95 4y agoI've never written a non trivial Makefile, because all my non trivial Makefiles are generated by CMake. I'm interested in why my CMakes are wrong since it's a way more complicated tool.
- kazinator 4y agoThere is a standard way to disable the builtin rules which is .SUFFIXES: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/make.html https://pubs.opengroup.org/onlinepubs/9699919799/utilities/m... .SUFFIXES Prerequisites of .SUFFIXES shall be appended to the list of known suffixes and are used in conjunction with the inference rules (see Inference Rules). If .SUFFIXES does not have any prerequisites, the list of known suffixes shall be cleared.
- yakshaving_jgt 4y agoI’m not convinced. The advice might be sensible in some way, but if Make isn’t anal enough for your tastes, why not just use a different build tool?
- orlp 4y ago> .ONESHELL ensures each Make recipe is ran as one single shell session, rather than one new shell per line. This both - in my opinion - is more intuitive, and it lets you do things like loops, variable assignments and so on in bash. This will at some point cause a bug that will be a pain to debug. Even worse when it depends on execution order/thread grouping with -j8. I would not consider introducing state is a good thing.
- asveikau 4y ago> .ONESHELL ensures each Make recipe is ran as one single shell session, rather than one new shell per line. This both - in my opinion - is more intuitive, and it lets you do things like loops, variable assignments and so on in bash. You can do things like loops and variable assignments, you just have to keep it on one line. You can put that one line on multiple lines using newline escaping by ending a line with \ You could argue that making it clumsy to do those things enforces that makefile scripts be simpler, while still providing enough of an escape hatch should it be necessary. But probably ONESHELL performs better. The default sounds like a lot of wasted repeated fork/execs.
- raggi 4y agoif you're building C please try to use the built-in rules. I often come across reimplementations that miss or disorder one of the flag sets and it becomes a pain when porting or integrating. General reminder: you don't even necessarily need a make file to build C: ~ % echo 'void main(){printf("hello");}' > example.c ~ % make example cc example.c -o example ... 2 warnings generated. ~ % ./example hello%
- bin_bash 4y agoThis is really cool! I've always worked in interpreted code so this isn't that useful to me but I'm surprised that it exists. I tried it myself though and it complains about not including stdio.h and wanting `int main` instead of `void main`. Though of course I still get your point.
- raggi 4y agoYes, I left out the warnings for brevity. It's a bit of a pain to do all the includes in a one-liner :) What this does allow you to do though, is also do things like: make CFLAGS=-DFOO LDFLAGS=-lfoo example
- ciarand 4y agoI don’t mind sticking to bash, but some of the other choices seem overly opinionated. In my experience the best Makefiles just invoke other commands, so you’re only using make for its dependency tracking. That way the Makefile remains fairly simple and you can use something more sensible (bash instead of bash-in-make, python, whatever) to write your actual build logic. The Makefile should probably be listed as a dependency for all the rules too, otherwise you’re gonna end up dealing with stale results and adding a “clean” target.
- dataangel 4y agoCan't believe the number of people s**** on this article who obviously don't have enough experience with build systems or the problems tab and space mixing cause. Quality has really gone down on HN comments in the last few years. Most of these choices are not really subjective. They are the most robust methods that make provides for trying to eliminate errors in your make file. If you think "set -e" isn't a good idea you're either a rookie or brain damaged. The alternative is your scripts silently look like they're working when they actually fail and you spend hours trying to debug where things went wrong because the system isn't pointing it out to you. My only fault with the article is that it's putting lipstick on a pig. None of this does anything to address the fundamental flaw with make that there is no protection against you incorrectly specifying building inputs. Every make file I've ever seen at a company at scale is buggy. These tips would definitely help but would not cure that fundamental issue.
- throwaway787544 4y agoNo company I have worked for in the past 10 years has used Make at all, other than when I'm using it, and that's because I'm just old and used to it. Developers use build tools designed for their language, Syseng use ci/cd and shell scripts, very large teams standardize on some "modern" overcomplicated monstrosity.
- dataangel 4y agoI agree the new systems are overly complex, but the reason they stick is enforcement of dependencies being accurate. If someone can bolt that into make it will be a huge improvement.
- kardos 4y agoAssuming that Makefiles aren't going away any time soon, I wonder if the situation here calls for a linter/formatter that can parse a makefile and optimize it toward "best practices"...
- shepherdjerred 4y ago> Make has a bunch of cryptic magic variables that refer to things like the targets and prerequisites of rules. I mostly think these should be avoided, because they are hard to read. > However, for the sentinel file pattern, the magic variable $(@D), which refers to the directory the target should go in, and $@, which refers to the target, are common enough that you quickly learn to recognize what they mean: So, avoid using the magic variables, but actually you should use them because they're useful and common. Got it.
- WesolyKubeczek 4y agoI think that this is one of actually nicer articles about using Make (and I think that developers should use it in more roles than a glorified task runner. It can do more, look at buildroot!), but the pontificating headline is quite off putting. I know that Twitter and Medium popularized this style a lot. I wish we used it less. The tips in the article are interesting, and the text is humbler than one would expect from a preamble like this, though. Go read it, give it a thought.
- morelisp 4y agoFor some reason RECIPEPREFIX has gotten really popular lately. Has OS X finally upgraded their version of Make, or are people no longer using the default Make on OS X at all?
- rcarmo 4y agoThe RECIPEPREFIX was my first clue that I should not go on reading. And yet, I did, and lo and behold, came to the comments page here to witness most people agreed. Seriously now I love Makefiles and use them extensively (nothing like make serve and make deploy to simplify my day), but there is a limit where being too opinionated (rather than just simple, easy to understand conventions) just ruins the tool and adds too much cognitive overhead.
- forrestthewoods 4y agoPlot Twist: your makefile is wrong because make sucks and is utterly miserable to use. Build systems are hard so I’m not gonna point fingers too hard. But reading/writing makefiles is one of my least favorite parts of the job.
- IncRnd 4y ago2019: An opinionated approach to GNU Make, https://news.ycombinator.com/item?id=21812656 https://news.ycombinator.com/item?id=21812656
- throwaway787544 4y agoPlease don't tell people "you are wrong / don't do X", especially if it's not objectively true. It's negative, rude, judgemental, and bossy. Find a kinder way to make your point and people might listen to you. (If you don't care if people listen to you, why are you speaking?)
- benreesman 4y agoThis is a really weird thread. It’s an opinionated blog post about relatively minor Makefile conventions with a clickbait title, which doesn’t look obviously self-submitted. And yet the thread is #1 and has 96 comments, of which like 92 are trashing the poor guy. Surely there is someone more worthy of an HN gang-tackle than this? It can’t be that slow of a news day.
- pessimizer 4y agoIt's triggering for some people to be told that they are wrong, even if the person who said it doesn't actually know them specifically, wasn't thinking about them as individuals when they wrote the blog, and has no idea that they exist. The idea that someone could be so presumptuous to assume that they had more make knowledge than everyone on the planet makes them very angry, because they are someone on the planet, so their first instinct is to lash out and prove that person wrong. A better instinct would be to humor the idea that the author doesn't think they know make better than anyone else in the world and isn't trying to hurt their feelings or their careers, but instead is trying to give people who don't know make as well as the author does a few tips. edit: basically a gathering of the people who reply to things on the internet that upset them with: "That's just your opinion." No shit, buddy, I wrote it, who else's opinion would it be?
- yjftsjthsd-h 4y ago> It's triggering for some people to be told that they are wrong, even if the person who said it doesn't actually know them specifically, wasn't thinking about them as individuals when they wrote the blog, and has no idea that they exist. ...and, of course, when they're not actually wrong. > No shit, buddy, I wrote it, who else's opinion would it be? Okay, if OP can have an opinion that "Your Makefiles are wrong", then I can have an opinion that " Your article is wrong". Fair?
- Joker_vD 4y agoYou see, the very first point of the article not only has a really weird opinion (that'd be only half bad), but but this weird opinion is being justified/defended with obviously faulty reasoning: "in the shell spaces matter, [so don't use tabs]. Instead, ask make to use > as the block character". Yeah, because ">" doesn't matter in shell, and there is absolutely no confusion possible as a result. Imagine copypasting a recipe from such a Makefile into a shell? The results will be pretty hilarious. Which is a shame because the rest of the post is mostly reasonable: turning recipes from a collection of one-liners into an actual piece of shell script, deleting output files on build errors, using -e and -o pipefail, etc.
- teddyh 4y agoThe style he advocates are still technically makefiles, in the same way that this is still technically Python: https://www.reddit.com/r/ProgrammerHumor/comments/uosex4/no_please_no/ https://www.reddit.com/r/ProgrammerHumor/comments/uosex4/no_...
- deleted 4y ago[deleted]
- telez 4y agouse grouped targets https://www.gnu.org/software/make/manual/html_node/Multiple-Targets.html https://www.gnu.org/software/make/manual/html_node/Multiple-... rather than sentinel.
- jlg23 4y agoI've heavily relied on GNUMake myself in a commercial project. But when I encounter a software that does require a GNUMake >= a specific version and on top bash instead of Posix SH, I must be quite desperate to install this just to build some software. My excuse was that I was building against 80 different target devices (it was j2me development) on some under-powered machines and that there was no budget for cutting down build times by several orders of magnitudes compared to ant. What's the author's excuse?
- admax88qqq 4y ago> GNUMake >= a specific version and on top bash instead of Posix SH It's such a shame that this is the attitude though. We're stuck with a make and shell frozen in time. Why even add new features to make/bash if nobody will run the new versions? Make is cool, but it's stagnated because people don't think we should rely on anything beyond POSIX
- jlg23 4y agoMy development environment is FreeBSD, OpenBSD and Linux; I am deploying to non-linux unixes only. 99% of the patches I have to do to build systems are one-liners that explicitly spell out what syntactic sugar the newest feature of gmake/bash supported or that fixes what a linux-only developer considers to be "the standard" (and let's talk real, it's not only "linux-only" anymore, one has to say "this-specific-linux-distro-only"). And that is on top of the fact that it is not about "running the new versions" but actually installing non-standard software.
- admax88qqq 4y agoSure but do you pull in any other libraries or dependencies in your development that are newer than the 90s? Why must the build system stick to POSIX when your libraries don't?
- Animats 4y agoThe trouble with makefiles is that they're supposed to specify dependency relationships, but they're used as a procedural scripting language because the dependency system isn't very smart.
- penguin_booze 4y agoI've been using make for a while now, but have never seen the $(@D) variant for target. I didn't know about .RECIPEPREFIX, either. Does anyone know if these were additions in the recent versions of make? I could only find release notes in the mailing list [0]. Initial searches didn't turn up anything. [0]: https://lists.gnu.org/archive/cgi-bin/namazu.cgi?query=gnu+make&submit=Search&idxname=info-gnu https://lists.gnu.org/archive/cgi-bin/namazu.cgi?query=gnu+m...