I'm trying to correctly express the dependencies for running yacc, which produces multiple targets from a single invocation. Let's start with a rule from racoon2's lib/Makefile.in:
.y.c: $(YACC) $(YFLAGS) $< mv -f y.tab.c cfparse.c
There are three problems with this:
- it has a hardcoded filename in a pattern rule,
- it has an intermediate file with a generic filename, which causes problems when run in parallel. I use make -j4...this gets run in parallel if there are multiple .y files, and in a non-obvious case I'll mention below
- It doesn't mention the y.tab.h target that other files (cftoken.o) depend on.
As far as I see, there's no way to express to make the generic intermediate file problem. The best you could do is to use lockfile(1). But it's not universally available, and if I'm going to try convincing a project to switch to tools not universally available, I might as well just try for bison, which produces unique filenames directly. Now my rule can look like this:
%.tab.c %.tab.h: %.y $(BISON) $(YFLAGS) $<
This works under GNU make, and almost works under BSD make. The problem there is that it's run twice. Easy to see with a test file:
.PHONY: all all: foo.out1 foo.out2
%.out1 %.out2: %.in lockfile -r0 mylock touch $*.out1 $*.out2 sleep 1 rm -f mylock
clean: rm -f mylock foo.out
It works with gmake but fails with bsdmake. And here's something odd: replace that pattern rule with a static one...
foo.out1 foo.out2: foo.in lockfile -r0 mylock touch foo.out1 foo.out2 sleep 1 rm -f lock
...and GNU make fails, too. A non-intuitive difference between static and pattern rules: static rules use multiple targets on a line to say that both targets are made with similar commands (but different $@), while a pattern one says that both targets are made with the exact same invocation.
What about cheating by making one target depend on another?
%.tab.c: %.y $(BISON) $(YFLAGS) $<
I thought about it, but there's no guarantee the targets are produced in a particular order, and if they happen in the one opposite what I give, it will rebuild. It might end up doing that over and over again if my choice is consistently wrong.
I guess what I can do is make cftoken.o depend on cfparse.tab.c, as a surrogate for cfparse.tab.h. It's silly, but it works.
My conclusion: make sucks, and GNU make sucks a little less than most. But I guess I already knew that from Recursive Make Considered Harmful. (Besides making the argument you'd expect from the title, it has some good points like how GNU make's reparsing of changed include files put it a step above the rest.)