24 Mar 2007 (updated 24 Mar 2007 at 10:17 UTC)
»
make oddities
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[12]
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) $<
%.tab.h: %.tab.c
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.)