Pergunta

I have this very basic example Makefile for compiling lex yacc projects.

all: y.tab.o lex.yy.o

lex.yy.c: calc.l
    #flex calc.l

y.tab.c y.tab.h: calc.y
    #bison -d calc.y

lex.yy.o: lex.yy.c y.tab.h
    #gcc -c lex.yy.c

y.tab.o: y.tab.c
    #gcc -c y.tab.c

However when I run make, I see that bison is being called twice. I'm assuming its once for each target y.tab.c and y.tab.h.

#bison -d calc.y
#gcc -c y.tab.c
#flex calc.l
#bison -d calc.y
#gcc -c lex.yy.c

This is causing an issue in parallel build. How do I modify the makefile so that the dependencies are the same as above, but bison is called only once to generate both the target files.

Foi útil?

Solução 2

The best answer to this dilemma, at least if you're using GNU make, is to use pattern rules. This does mean you can't call the output y.tab.c and y.tab.h; you need the output name to be a derivative of the input name. Suppose for calc.y you wanted to generate calc.tab.c and calc.tab.h; then you'd write:

%.tab.c %.tab.h: %.y
        bison $<

all: calc.tab.o

Pattern rules with multiple targets do exactly what you want: they tell GNU make that one invocation of the rule builds both targets. This ensures that parallel builds work properly without needing .NOTPARALLEL etc.

Outras dicas

If you want to really test what is going on, you have to provide commands that actually create your target files. #bison -d calc.y is a comment, so it is run first for "creating" y.tab.h because of the dependency of lex.yy.o and since no y.tab.c was created from that, it is run again to create y.tab.c because of the dependency of y.tab.o. A more realistic simulation could use touch to create the files:

all: y.tab.o lex.yy.o

lex.yy.c: calc.l
    #flex calc.l
    @touch $@

y.tab.c y.tab.h: calc.y
    #bison -d calc.y
    @touch y.tab.c y.tab.h

lex.yy.o: lex.yy.c y.tab.h
    #gcc -c lex.yy.c
    @touch $@

y.tab.o: y.tab.c
    #gcc -c y.tab.c
    @touch $@

create:
    touch calc.l calc.y

clean:
    rm y.tab.o lex.yy.o lex.yy.c y.tab.c y.tab.h lex.yy.o y.tab.o calc.l calc.y

With this setting, make "launches" bison only once, as expected.

#bison -d calc.y
#gcc -c y.tab.c
#flex calc.l
#gcc -c lex.yy.c

Note that this might not solve your issue with make -j, as in this case the creation of both y.tab.h and y.tab.c may be scheduled independently. A solution is to make only y.tab.c depend on calc.y, y.tab.h depending on y.tab.c with a recipe that does nothing, as in:

y.tab.c: calc.y
    #bison -d calc.y
    @touch y.tab.c y.tab.h

y.tab.h: y.tab.c
    :

Your problem is the line y.tab.c y.tab.h: calc.y which looks like a multi-target rule. But in fact it is a short-hand for two individual rules. Because traditional make is sloppy about where files come from, this often works out well enough. But in the parallel case it might not, as Virgile pointed out.

You do not have this problem if you use makepp, which really treats this as a multi-target rule. Also makepp is very precise about who builds what (because it keeps track of it for future builds, rather than being fooled by time-stamps). That is the only way it can provide the reliability it promises.

To execute some target not parallel, you can use .NOTPARALLEL Built-in taget -
http://www.gnu.org/software/make/manual/html_node/Special-Targets.html

all: y.tab.o lex.yy.o

lex.yy.c: calc.l
    #flex calc.l

y.tab.c y.tab.h: calc.y
    #bison -d calc.y

lex.yy.o: lex.yy.c y.tab.h
    #gcc -c lex.yy.c

y.tab.o: y.tab.c
    #gcc -c y.tab.c

.NOTPARALLEL : y.tab.c y.tab.h

.NOTPARALLEL

If .NOTPARALLEL is mentioned as a target, then this invocation of make will be run serially, even if the ‘-j’ option is given. Any recursively invoked make command will still run recipes in parallel (unless its makefile also contains this target). Any prerequisites on this target are ignored.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top