Hacker News new | ask | show | jobs
by saghm 567 days ago
> Do $@ and $< look arcane at first glance? Yes. Is what they do simple, though?

And yet, I still don't actually know what `$@` and `$<` actually mean outside of your example. I only can assume that somehow that substitutes in `foo.o` and `foo.c` because you literally put the non-magic versions right below it (and above it a few paragraphs above as well), but I'm honestly not even positive that I'd run `make foo.o` rather than `make foo` with your example, and I feel fairly confident in guessing that this would _not_ compile into a binary called `fooexec` like the target you defined above. I don't have an idea whatsoever what I'd need to put in the target to be able to substitute in something like that because the fact that the first `%` somehow becomes `$@` and the second becomes `$<` doesn't follow any obvious pattern I can use to figure out what the third symbol would be, or even if there is one.

The problem is that "simple" is not the same as "intuitive" or "discoverable". Yes, I could probably put in the effort to go and find out these things, but why would you expect someone to be motivated to do that by someone telling you that it's "trivial" and it "pains" them as they explain things in a way that doesn't actually clarify things in a way that helps me? If you actually want to try to make a strong case that this is something worth learning for people, repeatedly referring to it in ways like "very simple" and "so trivial" and "immediately obvious" is counterproductive.

As an aside, I think it's also a bit of leap to assume that they're "rebuilding everything all the time". It looks to me like the build.py script is only needed a single time to bootstrap from the original compiler to this one, and rebuilding every single C file in the new compiler rather than using the artifacts built with the original one is kind of the whole point. After that's done once, I don't see why the new compiler couldn't be used via the Makefile. If you're complaining about Python build scripts in general rather than this specific one, I don't know why you assume they can't be written to check the timestamps on the files to see if a rebuild is necessary before doing so. That's exactly what `make` does, and hey, it's a very simple concept!

3 comments

$@ is the name of the target -- the @looks a bit like an old-fashioned target for practice and competition shooting.

$< is the name of the input (the file the rule depends on). If the rule depends on more than one input, it is the first one. So you write your dependencies as xxx.c xxx.h yyy.h zzz.h with the C file first. Mnemotechnically, it is "the thing that goes into the build".

$^ is the names of all the files the rule depends on. Think of it as a large hat above all the input file names.

Many things in make are clumsy and GNU make is a behemoth that does so much more than the original make that it is really hard to learn -- but these shortcuts have really good names!

(They are GNU make extensions, btw.)

One might ask how you learned C (or python, or anything else) in the first place, if you can't be bothered to learn what the very simple $< and $@ mean, which would take you literally seconds to look up if my example wasn't actually enough already.

The rest is just... of course you can reimplement make, or a subset of it, in python, python is turing complete after all. But why? make isn't that arcane. I'm sorry it uses the weirdly looking $@ and $< for "target" and "dependency" respectively. Those two are extremely common and you have to type them in a lot of rules, so the authors chose a shorthand. One way or another your python make subset will have to bring in the same concepts they represent, too.

This is trivial in the sense that it's a part of any build system worth its salt. And I can almost guarantee you that OP for example, who wrote their own C compiler, understood it immediately.

> One might ask how you learned C (or python, or anything else) in the first place, if you can't be bothered to learn what the very simple $< and $@ mean, which would take you literally seconds to look up if my example wasn't actually enough already.

The problem is, you typically don't need to touch Makefiles that often once they work (or fulfil a reasonable interpretation of "work"), so unless you're a distro packager or the project(s) you work on are sufficiently complex and large to warrant an entire FTE just for build tooling, chances are high you need to touch that stuff only once every few years, by which time almost everyone has to dig into the documentation yet again... made worse by the fact that Google's search quality has gone down the drain.

I don't disagree that the concepts are simple; I'm arguing that the way they're conveyed in makefiles optimizes for the wrong thing by prioritizing tenseness over conveying useful information. The issue isn't that they "look weird", but that they give context that can be used to build additional concepts off of. A sibling reply to my comment mentions that `$<` only specifies the first dependency, and that there's a separate symbol for specifying all of the dependencies. That might be easy enough to remember after learning, but when someone is literally just learning how to write makefiles for the first time, it won't be obvious whether `$<` is used for only a single dependency or all of them, let alone what symbol to use for dealing with more than one.

My point is that concepts being simple doesn't mean that the choice of how they're represented doesn't matter. When people aren't bothering to learn something you think seems "simple", it's a mistake to immediately assume that it's due to laziness or incompetence rather than first trying to understand their motivations. Maybe they are just lazy or incompetent, but it's also possible that there's legitimate confusion around something that wouldn't occur to you without seeing it the way a beginner sees it, and there's room for improving how things are documented or taught.

There's a pattern of thinking that seems pretty common in our industry where we focus so much on the technical details of a system that the actual human experience of using it gets overlooked. My problem with this line of thinking is that value a tool is only realized when people actually use it. As a hypothetical, imagine that some new technical issue becomes commonplace and two different tools get written to solve the problem; the first tool solves the problem perfectly and as quickly as possible every time but doesn't get used by anyone, and the second tool does only an adequate job of solving the problem and requires a bit of manual additional work from the user to fix things, and that tool gets used by almost everyone. I'd argue that the first tool is not actually successful in any meaningful way because it doesn't actually help anyone with the problem that it was designed to solve.

To be clear, I'm not trying to say that no one uses makefiles or that they don't provide any real world value. I'm trying to say that if you're actually trying to get more people to use makefiles because they solve problems that everyone has better than what they're currently doing to try to solve them, the most effective way would be to look at it from the perspective of the people who aren't already using it and figuring out what's stopping them. When you feel strongly that it's the best way to do things, anybody who isn't doing things that way must see things differently, and the best way to try to change that starts by figuring out what that difference is.

If you don't actually care about trying to convince people and mostly just want to vent, then this advice won't be relevant, but if your goal is actually to try to change the way people do things, the approach you use to explain things is going to matter just as much as your knowledge about the things you're explaining. Just like how a brilliant professor who does groundbreaking research might be terrible at teaching an intro course to undergrads, it's possible to be an expert at something but not be effective at teaching it, and I think failing to appreciate the difference often ends up leading to a lot of frustration that would otherwise be avoidable.

Lots of things optimize for terseness. Assembly, arithmetic/algebra, and any kind of shorthand in any kind of trade. That’s because the shorthand is usually easy enough to learn, and then provides value going forward.

You are perfectly capable of parsing “3+2*4”, I don’t have to always write it as “MULTIPLY 2 BY 4, ADD 3”. Similarly you would get pretty mad if instead of “ld” or “lda” you’d have to type “load register” or “load accumulator” each time. Those concepts are trivial once you understood them, you don’t need a constant reminder of what they expand to.

Doing so might get slightly more people to “start using the thing” in the very first step, but might also let many people look for a more terse alternative. And we don’t have to cater to any person who is really unwilling to learn.

Overall I find it hard to buy that you actually had much trouble with $< and $@, at least in a way that you couldn’t have cleared up for yourself about 20 times in the time it took you to write out your comments.

I was hoping my introduction to Makefiles would be useful. If it was not, maybe I’m a bad educator after all.

Since you’re asking, $@ is the “target” of the build, that is, what you are aiming to build (left of the colon in the rule). This is why it looks like a bullseye.

And $< is of course the thing the target depends on. It’s the input, like standard Unix notation for stdin.

Hopefully this helps?