[HN Gopher] An opinionated approach to GNU Make
       ___________________________________________________________________
        
       An opinionated approach to GNU Make
        
       Author : hasheddan
       Score  : 155 points
       Date   : 2022-08-21 15:18 UTC (7 hours ago)
        
 (HTM) web link (tech.davis-hansson.com)
 (TXT) w3m dump (tech.davis-hansson.com)
        
       | [deleted]
        
       | kardos wrote:
       | Assuming 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"...
        
       | forrestthewoods wrote:
       | Plot 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.
        
       | andrewmcwatters wrote:
       | I 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.
        
         | jrockway wrote:
         | I don't know, I can't really argue with .SHELLFLAGS = -euo
         | pipefail.
         | 
         | If you want someone to really hate, pick me. I see a Makefile
         | and think "welp, strap in for a wild ride that isn't going to
         | get me a working binary". If someone has ever made a good
         | Makefile, I certainly haven't seen it.
        
           | shepherdjerred wrote:
           | Really? I love cloning a project and seeing a Makefile. The
           | install instructions are usually something like 'run make
           | install'.
           | 
           | mosh is a good example: https://mosh.org/#build-instructions
        
             | CamperBob2 wrote:
             | More typically, the install instructions read like the
             | bomb-defusal instructions in the classic MASH episode. "Cut
             | red wire, then unscrew detonator subassembly. But first..."
        
           | romanows wrote:
           | I don't know if it's a well-written makefile, but Python
           | builds are straightforward, even with stuff like LTO enabled.
           | Also sqlite.
        
         | agumonkey wrote:
         | I felt the opposite, I (among many) was winging my makefiles
         | due to unfinished learning. I always appreciate high precision
         | posts like these (even if some of the points are above the
         | top).
        
           | spicybright wrote:
           | It's high precision of a flawed view (in my opinion, of
           | course) that should only be taken for what they are, an
           | opinion.
           | 
           | For any readers that want to learn make, look at the
           | makefiles of big projects and see how they set things up.
           | There's a huge difference of one person's opinions vs. a
           | working system for a real world project.
        
             | falcolas wrote:
             | Or RTFM. Make is ridiculously well documented.
             | 
             | https://www.gnu.org/software/make/manual/make.html
             | 
             | It's a peeve of mine that folks refuse to read the actual
             | documentation for tools these days, preferring to wing it
             | by copying other people's code.
        
               | agumonkey wrote:
               | People should be a little more forgiving. Even after
               | reading emacs Manual thoroughly 3 times, I still managed
               | to miss a ton of nuances and information. Same goes for
               | make or other large old tools I guess.
        
             | pessimizer wrote:
             | Any specific criticism about any points made in the
             | article, or just general shade and a nonspecific
             | recommendation?
        
             | agumonkey wrote:
             | Hmm in a way it's difficult because either Makefiles are
             | small or suddenly part of very large projects with 1000+
             | lines nested Makefiles :)
        
         | dataflow wrote:
         | Honestly 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 wrote:
           | It 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 wrote:
         | That'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 wrote:
           | The 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 wrote:
             | They 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.
        
               | [deleted]
        
               | blueflow wrote:
               | I'm Alpine Linux user.... How would you feel if i said,
               | GNU/Linux users don't matter? Why should i bother to
               | support your platforms?
        
             | eropple wrote:
             | What about them? I don't use Python or Golang, but I
             | install the requested versions of each if I need to do
             | something that uses them. Folks who don't use GNU Make can
             | do likewise. It's not a big deal.
             | 
             | For me, every Mac I touch gets a GNU userland by default
             | (and Homebrew comes with shell helpers to switch to GNU
             | userland aliases temporarily, it isn't a hard switch
             | unless, like me, you lock it on all the time) because it's
             | really not worth the time and effort to remember what bits
             | are GNU and what bits are BSD. So I picked the one that has
             | the most stuff that makes my life easier.
             | 
             | There's also just the more basic thing of "the author
             | doesn't care about portability to environments they
             | consider out of scope," and that's totally fine, too. In
             | 2022, the barriers to entry to installing the necessary
             | bits of one flavor or another are very low, and if you
             | choose to make them harder for yourself that's mostly on
             | you.
        
               | bee_rider wrote:
               | It is of course up to the coder, what platforms they want
               | to support.
               | 
               | But it isn't _always_ easy to install new software. GNU
               | make is so widespread that it probably doesn 't matter.
               | But for example, if you are on a shared machine where you
               | might not necessarily have root. Or, if somebody requires
               | some obscure build system that isn't available in your
               | distro repos (of course you can build from source, but
               | the need to check the source of the build system is going
               | to increase the barrier to entry).
        
         | bee_rider wrote:
         | "Don't use tabs" just seems wrong.
         | 
         | "Use a recent bash" seems like a matter of preference.
         | 
         | "Make is all about files (paraphrasing)" seems a little obvious
         | hopefully, but definitely the way Make is intended to be used.
         | 
         | "Magic variables" eh... I dunno, I guess sometimes they can get
         | obscure but it isn't super hard to look this sort of thing up.
         | 
         | The omission of % seems weird.
        
           | dataflow wrote:
           | > "Use a recent bash" seems like a matter of preference.
           | 
           | Unfortunately it's not merely a preference; old Bash versions
           | (in particular the one that shipped on macOS for a long time)
           | have had at least one nasty behavior (I think multiple, but I
           | recall at least definitely one) that have been fixed since
           | then, but which cause grief in Makefiles. I don't recall what
           | it was or have a link handy, but if anybody knows, please
           | leave it here; it's a well-known issue.
        
           | jart wrote:
           | If you want to read something good, check out the docs for
           | this Landlock Make project I've been working on the past
           | week. https://github.com/jart/landlock-make It has many more
           | features since it was announced on August 7th.
        
             | bee_rider wrote:
             | Make, but protective. Ambitious, but pretty neat!
             | 
             | > .CPU = SECONDS
             | 
             | > .MEMORY = SIZE
             | 
             | I wish the cluster I use sometimes had this built into
             | their make system, to prevent people from make-ing on the
             | headnode, haha.
        
         | protastus wrote:
         | The attitude reminds me of the Nick Burns character from SNL.
        
         | omginternets wrote:
         | Tone aside, I agree. The net result is an unreadable mess of
         | niche configuration that could probably be avoided by having
         | the Makefile simply do less.
        
       | throwaway787544 wrote:
       | Please 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?)
        
       | IncRnd wrote:
       | 2019: An opinionated approach to GNU Make,
       | https://news.ycombinator.com/item?id=21812656
        
       | benreesman wrote:
       | This 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.
        
         | [deleted]
        
         | pessimizer wrote:
         | 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.
         | 
         | 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 wrote:
           | > 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 wrote:
         | You 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.
        
           | benreesman wrote:
           | Oh I also disagree with the ">" thing. I just think we could
           | have been like: "the > thing seems bad, good call on the
           | pipefail thing. moving on..." rather than like ruin this guys
           | day with 100+ negative comments because of the novel horror
           | of a clickbait blog title. It's not that big of a deal
           | (unless you're the guy who just got clobbered by every HN
           | user awake on Sunday morning).
           | 
           | It's just weird.
        
       | throwaway9870 wrote:
        
         | linkdd wrote:
         | There 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 wrote:
           | When 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 wrote:
             | > 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.
        
       | jlg23 wrote:
       | I'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 wrote:
         | > 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 wrote:
           | My 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.
        
       | shepherdjerred wrote:
       | > 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.
        
       | nrclark wrote:
       | This 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.
        
         | jwilk wrote:
         | > The -e flag is only useful because the author likes .ONESHELL
         | mode
         | 
         | Not really. The most common class of bugs I see in makefiles is
         | something like this:                 for x in foo bar baz; do
         | frobnicate $x; done
         | 
         | This ignores errors from frobnicate, unless you set -e.
        
         | jart wrote:
         | The 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.
        
           | nrclark wrote:
           | Woah, interesting! I didn't know that Make could launch
           | programs directly. Is that specific to GNU Make?
        
             | jwilk wrote:
             | At least NetBSD make (also used by FreeBSD, and packaged as
             | bmake in some distros) has the same optimization:
             | 
             | https://github.com/NetBSD/src/blob/netbsd-9/usr.bin/make/co
             | m...
        
           | jwilk wrote:
           | Doesn'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 wrote:
             | I 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 and
             | https://justine.lol/make/
        
       | bigcat12345678 wrote:
       | Looking 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).
        
       | Animats wrote:
       | The 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.
        
       | zzbn00 wrote:
       | A 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
       | 
       | In this way a meaningful change to the makefile triggers rebuild
       | automatically like it should
        
       | bee_rider wrote:
       | I 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?
        
         | zelphirkalt wrote:
         | > [...] 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.
        
           | bee_rider wrote:
           | >> 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.
           | 
           | Yeah, that part was a little bit flip. :)
           | 
           | I think neglecting this feature ignores too much of Make's
           | power, but it definitely does have other things going for it
           | as well.
        
         | jpollock wrote:
         | The tabs and spaces thing is targeted at teams. At any point,
         | there will be someone editing the makefile with a misconfigured
         | editor. Depending on the team's growth rate, and their desire
         | to use different tools, this happens a lot.
         | 
         | Avoiding tab vs spaces or tab width arguments is a good thing
         | for any team to do. :)
        
           | bee_rider wrote:
           | This is good, their builds will fail, notifying them of their
           | misconfiguration in the most obvious way possible. Hopefully
           | they will have to customize the Makefile a little to build on
           | their system, and give it a build before they start
           | programming, so they can correct their editor before they do
           | any damage to the source code.
        
             | usefulcat wrote:
             | > This is good, their builds will fail, notifying them of
             | their misconfiguration in the most obvious way possible.
             | 
             | I think it's very funny that you think a build is
             | guaranteed to fail in an obvious way due to a tab vs space
             | problem.
        
               | bee_rider wrote:
               | It may not be obvious _why_ it failed, but it should be
               | obvious that it did fail.
        
               | usefulcat wrote:
               | If, for example, make fails to update a target that
               | should have been updated, that kind of problem will
               | frequently not be obvious.
        
               | bee_rider wrote:
               | Sure, it also won't catch the misconfiguration for those
               | who don't even open the Makefile. It is too optimistic to
               | hope that this venerable piece of software will solve all
               | out misconfiguration woes, but at least it provides a
               | possible red flag sometimes.
        
           | aslilac wrote:
           | Yet this has been settled in Makefiles for a long time: use
           | tabs.
           | 
           | OP only seeks to sit on a high horse about how tabs are bad
           | and to divide an ecosystem which is already pretty
           | consistent. I'm so tired of people insisting tabs are evil
           | and that using them somehow makes you "wrong."
        
             | bee_rider wrote:
             | Tabs are obviously superior. If the tabs vs spaces argument
             | must be had, and the correct option somehow loses, then we
             | will have a much more annoying followup argument about how
             | many spaces (I vote for three spaces and I will
             | filibuster).
        
               | benreesman wrote:
               | Since we're doing this... ;)
               | 
               | I'm actually coming around on really narrow conventions
               | since tiny laptops started getting really good.
               | 
               | I used to think 80-wide columns and 1-2 space indent was
               | silly retro stuff in an era of modern displays.
               | 
               | But on an M2 Air you can just barely get 80 + 80 side by
               | side if you full screen SFMono at like 12-14pt.
               | 
               | I'll never sell my colleagues on it, but I've been
               | playing around with setting the formatters to 1-space
               | indent. It's not as heinous as it sounds.
               | 
               | Bring it on! :P
        
               | bee_rider wrote:
               | The cool thing about tabs is that you can do that 1-space
               | indent without spreading your madness (but yeah, I think
               | I got a preference for 3 spaces -- actually tabs with
               | tabwidth set to 3 -- while using a little, now old,
               | netbook).
               | 
               | One nice advantage of 3 space tabs is that if somebody
               | mixes tabs and spaces in Python, leading to mysterious
               | IDE-dependent bugs, it immediately sticks out (my example
               | is from helping student in an intro to python class,
               | hopefully this doesn't occur much in the real world).
        
               | Izkata wrote:
               | Holy crap I'm not the only one that likes 3...
        
               | benreesman wrote:
               | We've just gone the deterministic code formatter route on
               | everything. Right off the top of my head:
               | 
               | - C/C++: `clang-format` is _great_ - Rust: `rustfmt` is
               | very good - Python: `yapf` is very good - Java: `google-
               | java-formatter` is pretty good - Haskell: `fourmolu` is
               | OK, and if you apply it first and then `stylish-haskell`
               | it 's good enough - Starlark - `buildifier` is _great_ -
               | Shell: `shfmt` is pretty good - Nix: `nixfmt` is pretty
               | good
               | 
               | I haven't done any JS or TypeScript or golang in awhile,
               | but I'm sure there are great options there too.
               | 
               | By having all the auto-formatters, we can have one golden
               | config that's fully deterministic for upstream, but
               | everyone can have their own if they want and it just gets
               | blasted into the "golden" format before code review.
               | 
               | Finally, a world without brace wars! Everyone tabs and
               | spaces and whatever to their hear's content. Everyone's
               | happy!
               | 
               | * you do eat the blame getting fucked up the one time.
               | it's worth it.
        
               | int_19h wrote:
               | You can't meaningfully filibuster rustfmt / go fmt /
               | Black / ... - that's the best thing about them.
        
         | yjftsjthsd-h wrote:
         | > 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 wrote:
         | And 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.
        
       | bin_bash wrote:
       | I 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 (though I haven't had a chance to
       | use it myself yet).
        
         | brabel wrote:
         | > 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/
        
           | bravetraveler wrote:
           | Keep in mind I'm about as ignorant of Make as one can be -- I
           | use it to build things, but I've never put things into it
           | 
           | I have a feeling meeting this may scope creep the thing into
           | being more environment aware. Interpretation of the things
           | it's sourcing [and their paths], and less-than-obvious things
           | like environment variables
           | 
           | This just feels like one of those things trying to be too
           | helpful that inevitably gets in my way
        
           | marcosdumay wrote:
           | > which means you think that your tests are not deterministic
           | 
           | This would be relevant if the command was saving some test
           | report somewhere. But then the target would just depend on
           | the report, and there would be no need to add guard files.
           | 
           | Looks like that `make test` just does the normal thing that
           | is run the tests and print the results to the screen. If so,
           | people would want to repeat it any number of times.
        
           | bin_bash wrote:
           | > 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?
        
             | tirpen wrote:
             | Just run                   make -B test
             | 
             | to force a rerun?
        
               | bin_bash wrote:
               | First, the engineer needs to understand how the makefile
               | is written and that it doesn't rerun without changes. Is
               | it not rerunning because of npm? because of jest? If the
               | engineer isn't the one that wrote the makefile: they
               | won't.
               | 
               | Next, you're assuming a JS engineer would know what the
               | flag is (or even that such a flag exists) to force
               | reruns. I've literally never used this flag in my (JS)
               | career so I wouldn't expect anyone to know this.
               | 
               | Don't send engineers down some debugging rabbit hole just
               | to save a few seconds when no changes happen. If you want
               | this functionality, just use `jest --onlyChanged` in your
               | own workflow and don't screw with everyone else's.
        
               | gpderetta wrote:
               | On non-toy projects it will save minutes if not much
               | more. It also encourage good test discipline l.
               | 
               | The engineer will know because it is their job to know.
               | 
               | (Yes I'm a huge fan of deterministic tests and running
               | only what's necessary).
        
           | fn-mote wrote:
           | > [...] which means you think that your tests are not
           | deterministic, otherwise it would be completely pointless to
           | run them again without inputs changing.
           | 
           | Re-running seems reasonable to me. It's make, not Bazel.
           | 
           | In the article I'm looking at targets with a "find" shell
           | command on the right hand side. I don't think the
           | dependencies of those files are carefully mapped somewhere
           | else in the makefile.
        
       | yjftsjthsd-h wrote:
       | Title: "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 wrote:
         | The 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 wrote:
           | Clickbait _working_ isn 't really a great defense.
        
           | flykespice wrote:
           | The 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 wrote:
             | In 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 :)
        
       | mkl95 wrote:
       | I'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 wrote:
       | There is a standard way to disable the builtin rules which is
       | .SUFFIXES:
       | 
       | 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.
        
       | teddyh wrote:
       | The 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_...
        
       | leononame wrote:
       | I 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 wrote:
       | Previous discussion (2019) with 194 comments.
       | 
       | https://news.ycombinator.com/item?id=21812656
        
       | WesolyKubeczek wrote:
       | I 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.
        
       | blibble wrote:
       | the 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 wrote:
         | Many 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.
        
       | cpuguy83 wrote:
       | How 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 wrote:
         | Can 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 wrote:
           | Not 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 ---
        
       | jp57 wrote:
       | The 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.
        
         | krinchan wrote:
         | And I think it's important to read the article before writing
         | comments based purely on the title, but here we are.
         | 
         | The author makes several convincing arguments and specifically
         | lays out what failure modes are avoided for each
         | recommendation. Honestly the title is completely out of step
         | with the tone of the argument, which is a critique I can
         | support.
        
         | Tainnor wrote:
         | I agree that the "you're doing it wrong" tone of the article
         | title is off-putting and that if you're just using Make for
         | some minor automation once in a while here and there, you
         | probably shouldn't worry, but I found most of the tips
         | genuinely helpful and the reasons for doing so are stated or
         | obvious.
        
           | systemvoltage wrote:
           | Should just be titled "I'm doing it this way, adopt what you
           | like".
        
         | dwheeler wrote:
         | > 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.
         | 
         | I agree, any claim that you should do XYZ should give a strong
         | argument.
         | 
         | The article here _does_ try to give very short arguments, to be
         | fair. I leave unconvinced by many of them. For example,
         | requiring bash means you can 't use dash; dash is less capable
         | but much faster.
         | 
         | I prefer arguments that walk through the key pros and cons.
         | Longer, but in long run more useful.
        
           | wk_end wrote:
           | > The key message here, of course, is to choose a specific
           | shell. If you'd rather use ZSH, or Python or Node for that
           | matter, set it to that. Pick a specific language so you can
           | stop targeting a lowest common denominator
           | 
           | Seems like dash would be fine for the author.
        
         | [deleted]
        
         | fpoling wrote:
         | The article does state the reasons for the rules and gives
         | examples how not following them may lead to troubles.
        
         | [deleted]
        
         | avg_dev wrote:
         | I 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 wrote:
           | > 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.
        
         | [deleted]
        
         | dataflow wrote:
         | I don't understand your complaints.
         | 
         | > it's incumbent upon him to clearly state what failures I will
         | avoid
         | 
         | He does _exactly_ that though? Here 's a list of some of them:
         | 
         | Rule: "Use a strict Bash mode"
         | 
         | Failure(s) avoided: _" your build may keep executing even if
         | there was a failure in one of the targets."_
         | 
         | Rule: .ONESHELL
         | 
         | Failure(s) avoided: _assignments failing to take effect on
         | subsequent lines_ ( "it lets you do things like loops, variable
         | assignments and so on in bash")
         | 
         | Rule: .DELETE_ON_ERROR
         | 
         | Failure(s) avoided: _" ensures the next time you run Make,
         | it'll properly re-run the failed rule, and guards against
         | broken files"_
         | 
         | Rule: MAKEFLAGS += --warn-undefined-variables
         | 
         | Failure(s) avoided: _avoids silent misbehavior when a variable
         | doesn 't exist_ ("if you are referring to Make variables that
         | don't exist, that's probably wrong and it's good to get a
         | warning")
        
           | mayoff wrote:
           | .DELETE_ON_ERROR is not sufficient. Some day your make
           | process will be killed (due to a bug, OOM, kernel crash,
           | power loss, whatever) while the recipe is running, thus
           | having no chance to delete the broken output.
           | 
           | I had this happen often enough (in a quite large system that
           | farmed out compile jobs to a cluster) that now I always make
           | my recipes write to a temporary file, and then rename the
           | temp file to the actual target, e.g.
           | 
           | test.o: test.c > cc -o $@.tmp $< > mv $@.tmp $@
           | 
           | Once you adopt this strategy, .DELETE_ON_ERROR is irrelevant.
        
             | dataflow wrote:
             | > .DELETE_ON_ERROR is not sufficient. Some day your make
             | process will be killed (due to a bug, OOM, kernel crash,
             | power loss, whatever) while the recipe is running, thus
             | having no chance to delete the broken output.
             | 
             | I agree, it's one reason Make itself sucks.
             | 
             | > I always make my recipes write to a temporary file, and
             | then rename the temp file to the actual target
             | 
             | Then hopefully set the timestamp if you used some tool to
             | generate it so it doesn't look out of date to Make. (Again,
             | another deficiency of Make. I could list more.)
             | 
             | > Once you adopt this strategy, .DELETE_ON_ERROR is
             | irrelevant.
             | 
             | Sure (well actually no, but that's next paragraph), but
             | that strategy is only worth it for "serious" Makefiles.
             | Ones you use in your work environment and all. For personal
             | projects etc. it's not always worth the hassle of polluting
             | the Makefile with boilerplate like that; it's much handier
             | to put that one line.
             | 
             | But actually no, there's still a benefit: it saves disk
             | space to delete incomplete output files. If your files are
             | 4KiB you might not care, but if they're 4GiB then you
             | might. And sure you can get around that by manually adding
             | 'rm' in the beginning of every rule too, but why not use
             | this instead while it's already there. It's one line and
             | doesn't harm anything.
        
         | mooselaker wrote:
         | It literally says at multiple points throughout "this is not
         | dogma", including the entire final section.
        
           | bin_bash wrote:
           | "Your Makefiles Are Wrong" is an incredibly dogmatic thing to
           | say despite that disclaimer.
        
             | cinntaile wrote:
             | Some people can't recognize a clickbait title when it hits
             | them in the face.
        
             | saulpw wrote:
             | Titles are clickbait, not dogma.
        
         | [deleted]
        
       | telez wrote:
       | use grouped targets
       | https://www.gnu.org/software/make/manual/html_node/Multiple-...
       | rather than sentinel.
        
       | morelisp wrote:
       | For 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?
        
       | chrismorgan wrote:
       | > _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.
        
       | jchw wrote:
       | This 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.
        
         | hedora wrote:
         | This is a sadly common corrolarry to "those who do not
         | understand make are destined to reimplement it poorly".
         | 
         | I strongly suggest spending an afternoon with "recursive make
         | considered harmful" and the gnu make manual.
         | 
         | I've never encountered a cmake proponent that can add trivial
         | functionality to a cmake build in less time than it took me to
         | learn make.
         | 
         | I can usually port cmake builds to make in less time than such
         | people can debug the cmake version of the build I ported.
        
           | jchw wrote:
           | I have one question to ask you.
           | 
           | How do I do this in Make:
           | find_package(OpenGL)              target_link_library(app
           | OpenGL::OpenGL)
           | 
           | Goals:
           | 
           | - Support Windows, macOS, *NIX like
           | 
           | - Compile with either MinGW or MSVC on Windows
           | 
           | - Decent error output if GL headers are not found
           | 
           | This SHOULD be possible. All we are doing here is calling the
           | compiler with a fairly easy to derive set of options. Yet, in
           | Make, there is no ideal way to abstract this. Even detecting
           | platforms can be annoying, less trying to abstract the
           | differences between them.
           | 
           | I do not find Make hard to use. I do not lack knowledge on
           | how to use Make, or not understand the "zen" of Makefiles.
           | 
           | I just know that `-lGL` is a terrible answer to this
           | question. And I find Make to be generally bad for compiling
           | software.
           | 
           | Out of tree builds and reasonable platform detection
           | generally require configure scripts, at which point we've
           | thoroughly left any semblence of elegance.
        
             | hedora wrote:
             | You should either trust the OS to package GL, or vendor it
             | yourself.
             | 
             | If you trust your build environment, then -lGL is the right
             | answer. If you do not, then vendoring libGL is the right
             | answer. Both approaches are easy to achieve with make.
             | 
             | find_package semantics are frankly weird: "maybe use the OS
             | version, or override in nonstandard ways, or maybe download
             | some version from somewhere and build it using some
             | compiler flags that came from somewhere mysterious. If you
             | succeed, have package-dependent side effects on the set of
             | global variables in my cmake script".
             | 
             | Does it have a higher chance of producing a binary in dodgy
             | environments? Sure. Are those binaries actually
             | reproducible or what the developer / distribution tested
             | with? Absolutely not.
             | 
             | As for windows with visual studio: Either point make at the
             | visual studio compiler, or hand maintain a separate .SLN
             | file. The impedance mismatch between Unix and Windows
             | builds is too great, and the auto-generated .SLN files that
             | tools like cmake produce are low quality.
        
         | WesolyKubeczek wrote:
         | Make can do more than compile a bunch of C files. If you can
         | express your goals and dependencies as files with meaningful
         | creation timestamps, it can be a potent task executor that can
         | also skip over steps if they are already done.
         | 
         | Also, your CMake generates Makefiles, so...
         | 
         | CMake and meson/ninja, though, seem to be pretty much tuned to
         | compiling C-shaped things, although I'd like to see them
         | (ab)used for other things.
        
           | gpderetta wrote:
           | Incidentally, is there such a thing as a distributed Make
           | that can run and check the dependencies across machines?
           | 
           | Of course Make + ssh and a distributed FS gets you there, but
           | you don't always have a distributed FS especially across
           | continents.
        
         | gpderetta wrote:
         | I tollerate CMake because a bad standard is still better than
         | no standard, but I would chose Make everyday if it was my
         | decision and just for me.
        
         | pfranz wrote:
         | Out 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 wrote:
         | I thought most people use ninja through CMake? Do people
         | actually write ninja scripts directly?
        
         | benreesman wrote:
         | I'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/138064827250353...
        
           | jchw wrote:
           | The 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.
        
             | benreesman wrote:
             | Oh I agree mostly. CMake is still a practical necessity for
             | the time being, and I also agree that from 3.10 or so it
             | goes from an NC-17 slasher flick to an R-rated horror film.
             | It's so ubiquitous that "I will do nothing, because I can
             | do nothing".
             | 
             |  _Personally_ I've taken the plunge on Bazel and whoo, the
             | first time you run clang-format, save, and hit in the cache
             | on the .o, I mean sex is cool but have you tried building
             | C++ fast?
             | 
             | But Bazel will probably never be the standard or even
             | common, le sigh.
             | 
             | What we might get is something sane that generates CMake,
             | so that it can generate Ninja, so that we can be bitching
             | about CMakeGen in 10 years.
        
               | jchw wrote:
               | Totally fair. I agree. It is a damn shame that things are
               | the way they are with build systems.
               | 
               | I like Bazel, but I wish it didn't inherit all of the
               | issues that come with large Java software. Also, to be
               | honest, I'm less thrilled with how Bazel works outside
               | Google than Blaze works within Google; they took out some
               | of it's advantages in exchange for better ecosystem
               | interoperability, which totally makes sense and yet also
               | is a bummer. I wish they could've somehow given the rest
               | of the world a generalized taste of how they do it.
               | 
               | Sorta related: I like Bazel's concept of the build
               | server. I can't help but think the programming community
               | could invent a "build server protocol" not unlike the
               | language server protocol, and somehow integrate it with
               | LSPs and IDEs. (Obviously it would still be complex, but
               | the premise of having a somewhat general way to swap
               | build systems in a project and have e.g. clangd or
               | tsserver know what flags it would get where seems
               | amazing.)
        
               | benreesman wrote:
               | That build server protocol is a great idea. There are
               | folks who sell Bazel build as a service (e.g. BuildBuddy
               | I think it's called), but while it's pretty easy to get a
               | remote action cache, it's quite a bit more involved to
               | get a true remote farm, and this obviously goes nonlinear
               | in complexity as languages/platforms/toolchains start
               | their combinatorics routine. If there was a standard and
               | it were as successful as LSP (thank god for VSCode and
               | whatever hero at Microsoft decided to keep LSP despite
               | the JSON) I think it would create a whole new SaaS
               | ecosystem and make everyone's life better.
               | 
               | I've never worked at Google, so I'm very curious about
               | what Blaze gets you that Bazel doesn't. I have no trouble
               | imagining it's a lot: you can do great stuff when you
               | control the whole stack and have lots of computers, but
               | I'd be intrigued by any details you're at liberty to
               | share.
        
       | kennu wrote:
       | > .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 wrote:
         | Also, 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 wrote:
           | Are 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.
        
             | yjftsjthsd-h wrote:
             | Are there any major editors that can't handle this? Given
             | that python has a very similar kind of formatting I would
             | assume any editor that a programmer is using today can
             | handle it.
        
               | int_19h wrote:
               | In Python, if you save your file with tabs as spaces, the
               | code 1) still works, and 2) does the same thing it did
               | before. Furthermore, it prohibits mixing tabs and spaces
               | up in the same file, on the basis that it's impossible to
               | reliably determine indentation levels then. So this isn't
               | really a major issue with Python, unlike Make.
        
               | yjftsjthsd-h wrote:
               | Sure, I'm happy to agree that the original design of make
               | is kind of questionable. My point was that an editor
               | which can handle telling you to not mix tabs and spaces
               | in python should also be able to tell you to use tabs in
               | a Makefile. Or, if you prefer, that if python can say
               | "only tabs xor spaces" and be seen as reasonable, then
               | make can say "only tabs" and be equally reasonable.
        
             | eropple wrote:
             | Just 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 wrote:
               | > 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).
        
               | eropple wrote:
               | Yeah, the two places I find myself still reaching for vim
               | are Git commits and quick shell/Makefile tweaks and both
               | are correctly formatted out of the box (shortened Git
               | first lines on commits, etc).
               | 
               | I do not think this is actually a problem.
        
               | falcolas wrote:
               | Even IntelliJ handles makefile whitespace correctly.
               | Folks using Notepad?
        
             | dharmab wrote:
             | Autoformatters like gofmt and black let you fix everyone's
             | editors at once. Though I haven't seen one for Make.
        
             | bee_rider wrote:
             | I'm not 100% certain, but I suspect a Makefile with this
             | error will become unusable. So, no need to fix their
             | editor, they will fix it themselves after their builds
             | fail.
        
       | yakshaving_jgt wrote:
       | I'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?
        
       | blueflow wrote:
       | This 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.
        
         | brabel wrote:
         | What 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).
        
         | yjftsjthsd-h wrote:
         | Given 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 wrote:
           | Quite, 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 wrote:
             | For the purpose of self-promotion, i guess. Being
             | confrontational brings attention.
        
       | orlp wrote:
       | > .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.
        
       | eqvinox wrote:
       | Wow. 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.
        
         | dllthomas wrote:
         | > Now I can't copy & paste a block anymore (into the shell, to
         | run it)
         | 
         | You likely can, actually. Most terminal emulators have a key
         | you can hold (ctrl in gnome-terminal, alt in urxvt) to select a
         | block of text that doesn't start at the beginning of the line.
         | Doesn't work if your lines wrap, of course.
        
           | spicybright wrote:
           | But then you can't copy a target and it's steps in one go.
           | And the pasted code will only have a single space for
           | indentation, if I'm understanding right.
           | 
           | Plus without word wrap you can't copy more than a screen
           | width at a time on most terms.
        
             | dllthomas wrote:
             | > But then you can't copy a target and it's steps in one
             | go.
             | 
             | The parent said they wanted to paste it into the shell to
             | run it. In that case, you aren't going to want to copy the
             | target, only the steps. It will have a single space
             | indentation if you start there, or no indentation if you
             | start one character over; either could be what you want,
             | depending on your shell settings and whether you want the
             | commands in your history.
             | 
             | If you want to copy and paste the target and all of its
             | steps in one go, you need to ask yourself where you want to
             | paste it, but probably including the > and copying normally
             | is the right thing, to be reformatted on the other side as
             | desired. In fact, I expect that to break a little less
             | often than pasting things with leading tabs.
             | 
             | > Plus without word wrap you can't copy more than a screen
             | width at a time on most terms.
             | 
             | Yeah, I was imprecise; "doesn't work if your lines wrap"
             | should probably have been "doesn't work if your lines are
             | long enough that they would need to wrap".
        
         | jen20 wrote:
         | > And the Makefile is now non-portable.
         | 
         | The article calls out GNU Make, so almost everything else in
         | there is also non-portable.
        
           | eropple wrote:
           | Even 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 wrote:
             | And in this case we're concerned about computers that do
             | have GNU make but don't have bash...
        
             | eqvinox wrote:
             | If 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.
        
               | eropple wrote:
               | Ehh. If I'm writing a Makefile, team context or no, it's
               | almost certainly because I don't expect other people to
               | be more familiar with Make than I am. If anything, I
               | would expect ONESHELL to map to how most programmers
               | think Make already works, even though it doesn't. I know
               | I have to go look up the various Make-isms whenever I
               | need to do anything complex.
               | 
               | And a Make supergenius, should I ever meet one, surely
               | will look at the top of the file or derive from
               | "incorrect" code that something is nonstandard.
               | 
               | I'd love better tools that take the good parts of Make
               | (around file transformations, mostly) and expose them
               | more effectively in shells (because doing so in a more
               | fully featured programming language often results in
               | different and worse hacks--hi, Rake) but using Make to do
               | things more people will find predictable seems fine to
               | me.
        
               | eqvinox wrote:
               | I guess I should've been more clear. My point is other
               | people /using/ your Makefile, not editing it. You know
               | what you're running. You don't know what everyone else is
               | running. I've singled out ONESHELL because it will be
               | _silently_ ignored by non-GNU make, and it will behave
               | ever so slightly different enough to cause extremely hard
               | to find bugs.
               | 
               | Example:                 .ONESHELL:
               | sometarget: someinput       BASEDIR=other_expression
               | SHELLVAR=complicated_expression       rm -rf
               | $$BASEDIR/$$SHELLVAR
               | 
               | (obviously extreme to illustrate the point, but you get
               | the idea.)
        
               | eqvinox wrote:
               | btw. On the BSDs, GNU make is "gmake" while plain "make"
               | is POSIX-compliant BSD make. Better hope you never make
               | the easy mistake of forgetting that "g"...
               | 
               | (If you use any of these features -- _please_ rename your
               | Makefile to GNUmakefile. _Please. I beg you._ )
        
           | eqvinox wrote:
           | > The article calls out GNU Make, so almost everything else
           | in there is also non-portable.
           | 
           | My brain honestly didn't process that, and it's still
           | refusing to. I think it's the braces around (GNU) -- seems
           | like I have some neurons wired to push (bracketed) pieces of
           | text down into a "detail" stack and eliminate them from high-
           | level processing...
           | 
           | P.S./ed.: GNU make looks for GNUmakefile before Makefile, and
           | arguably if the Makefile is GNU specific, that feature should
           | be used -- and that includes the title of the article ("Your
           | GNUmakefiles Are Wrong")
        
       | [deleted]
        
       | asveikau wrote:
       | > .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 wrote:
       | if 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 wrote:
         | This 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.
        
       | coliveira wrote:
       | This 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.
        
       | rcarmo wrote:
       | The 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.
        
       | ciarand wrote:
       | I 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 wrote:
       | Can'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 wrote:
         | No 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 wrote:
           | I 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.
        
       ___________________________________________________________________
       (page generated 2022-08-21 23:01 UTC)