A while ago, I was working at a company in South Florida. And I happened to be reading the manual for their build tool, which was called ‘make’, and it mentioned a special syntax available for cases where more than one output file could be built by a single application of a rule.
The example most programmers will understand today is with a yacc-like tool (or lex, for that matter):
y.tab.c y.tab.h : whatever.yacc
$(YACC) $(YFLAGS) $<
If you’ve ever used yacc, or the GNU equivalent, bison, or any other code generator, you’ll understand the pattern. There’s a set of input files, usually either just one, or one plus some other files generated by a lexer generator. And the input files are read by the code generator tool, and the code generator tool produces a bunch of different output files all at the same time.
The syntax that this ‘make’ tool I was reading about used to indicate this was a plus sign:
y.tab.c + y.tab.h : whatever.yacc
And that plus sign was enough to let the ‘make’ tool know what was going on with the simultaneous generation of both files.
The company in question was not a client. It was my second serious job after college. I started working there in the (late) 1980’s. And the ‘make’ tool in question was make, as provided with SunOS 3 and 4. (Not Solaris. That came later.)
I told that story for a reason: to point out that this somewhat niche-market problem has been solved, in a widely-available system, using a trivial syntax enhancement. The solution has been around for more than 20 years. And GNU make still gets it wrong.
The latest GNU make version provides a feature that almost solves the same problem. Except that it doesn’t. And that “feature” is a hack.
It’s documented in the manual, but not mentioned in a header in the contents, so you have to just know, or have someone give you a link. Google won’t help you find it. (It’s here.)
And of course the syntax used for the GNU make not-quite-but-maybe-almost hack is: two targets separated by spaces.
That’s right, the GNU version looks like:
a b : input
Which you might find confusing, because the GNU make syntax for two targets that do not all get regenerated at the same time, but instead require the recipe to be executed once for each output looks like:
a b : input
See the difference? Of course not, because at this level of abstraction there is no difference. The rules look exactly the same.
If you don’t know the secret – which is that the run-once or run-multiple-times behavior depends on whether the rule is a pattern rule or not – then you’re SOL, because there’s no visible indicator.
It’s worth pointing out that there’s a method to the madness of the GNU make weenies. The justification, if I were dumb enough to ask for one, would be something like this: the only time this kind of thing ever arises (in C) is when you’re using yacc or rpcgen. (Rpcgen is the reason the Sun guys added the rule, I’m fairly sure.) And yacc/rpcgen always get used in such a way that the filenames always have a common root. So making this incredibly lame hack makes total sense, because nobody ever does this, but if they did they’d be happy it was this way.
I don’t do a lot of C programming any more. There are other languages that are generally much more suited to what I’m trying to do. Some of those languages are “static” languages, like C++, Java, and D. And some of those languages are “dynamic” languages, like sh, perl, and scheme.
But in both camps, you’ll find languages that use directory names and file names to reflect the classes, modules, packages, units, or whatever-it’s-called defined inside them.
This is important because if I’m going to generate code, I want to be free to generate code using file and directory names that are meaningful to, and constrained in, the context of the programming language I’m actually using.
I really, really, really don’t want to have to conform to a naming convention inspired by a tool I’m not using in a language I don’t care about that is the basis for a syntax hack in make!
Ultimately, I think this is an example of a couple of things. First, that the GNU make guys are suffering a vision problem because they’re focused on building a core set of applications, using a small set of languages.
Second, I think they’re afraid to admit that other people, when faced with a similar set of problems as them, have come up with useful solutions. Back when GNU make got started, SunOS was the dominant Unix. And the guys at Sun were doing a fine job of carrying forward the tool suite. But heavens! For GNU to copy a useful feature of a popular tool would be to admit that we aren’t the sole source of clever ideas. Oh, noes!
Thus, we have the open source curse: Gnot Invented Here syndrome.
And also thus – more of a pain in my tuchus, thus – we have a version of GNU make that forces me to jump through a bunch of hoops to accomplish something that I could have done in two lines back in 1988.