Home > Software design >  Makefile tries to compile one of it's own rules as if it were a file
Makefile tries to compile one of it's own rules as if it were a file

Time:11-17

I have a simple makefile to compile some C code that uses OpenGL libraries, however, it appears to be interpreting one of it's own rules as a file to compile. The simplified version is as follows:

CC = gcc
COMPILEFLAGS = -Wall -lglut -lGL -lGLU -lm -c
LINKFLAGS = -lglut -lGL -lGLU -lm

castle: castle_link

run_castle:
    ./castle $(ARGS)

castle_link: castle_compile
    ${CC} -o castle castle.o ${LINKFLAGS}

castle_compile:
    ${CC} ${COMPILEFLAGS} castle.c

clean:
    rm *.o castle

Executing make -n castle yields the following:

gcc -Wall -lglut -lGL -lGLU -lm -c castle.c
gcc -o castle castle.o -lglut -lGL -lGLU -lm
gcc   castle.o castle_link   -o castle 

And I cant understand why this happens. On this very same file there is another set of rules that follow precisely the same pattern, only changing file names, that work flawlessly. (This only happens when executing the rule make castle, manually executing castle_compile and then castle_link does not cause that).

Any help is very much appreciated.

CodePudding user response:

To explain what happened, you have a rule without a recipe:

castle: castle_link

which means that make will try to find an implicit rule for it. From the manual:

If none of the explicit rules for a target has a recipe, then make searches for an applicable implicit rule to find one

And earlier in that page it says that prerequisites for targets are combined:

One file can be the target of several rules. All the prerequisites mentioned in all the rules are merged into one list of prerequisites for the target.

The implicit rule that matches will be either:

%: %.o
    $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@

if the .o file exists or:

%: %.c
    $(LINK.c) $^ $(LOADLIBES) $(LDLIBS) -o $@

if it doesn't. (make -p will print definitions of the implicit rules).

So the prerequisites for target castle will be:

  • either castle.o castle_link
  • or castle.c castle_link

And the $^ variable in the recipe from the implicit rule will put them in the cc command to execute. Let's see with this simplified makefile:

castle: castle_link

castle_link: castle_compile
    $(CC) -o castle castle.o -lm

castle_compile:
    $(CC) -Wall -c castle.c

If we execute it on a clean directory (i.e. with no castle.o):

]$ make --debug=m castle
...
    Must remake target 'castle_compile'.
cc -Wall -c castle.c                                # This is our recipe, notice -Wall
    Successfully remade target file 'castle_compile'.
  Must remake target 'castle_link'.
cc -o castle castle.o -lm                           # This is our reicpe, notice -lm
  Successfully remade target file 'castle_link'.
Must remake target 'castle'.
cc     castle.c castle_link   -o castle             # This is implicit rule with castle.c
cc: error: castle_link: No such file or directory

Now that castle.o was created, we execute it again:

]$ make --debug=m castle
...
    Must remake target 'castle_compile'.
cc -Wall -c castle.c                                # This is our recipe, notice -Wall
    Successfully remade target file 'castle_compile'.
  Must remake target 'castle_link'.
cc -o castle castle.o -lm                           # This is our recipe, notice -lm
  Successfully remade target file 'castle_link'.
 Prerequisite 'castle_link' of target 'castle' does not exist.
Must remake target 'castle'.
cc   castle.o castle_link   -o castle               # This is implicit rule with castle.o
cc: error: castle_link: No such file or directory

As you can see, in the first invocation it was "cc castle.c castle_link -o castle" and in the second it was "cc castle.o castle_link -o castle".

Don't ask me why only the second invocation printed "Prerequisite 'castle_link' of target 'castle' does not exist." :-)

  • Related