nowox
nowox

Reputation: 29066

Issue with make

Let's consider this tiny example. Everything should work fine without the VILLAIN. Actually this word leads to an error but not where I was expecting it...

The error:

$ make
mkdir -p obj
zip out.exe obj/foo.o obj/bar.o obj/baz.o
        zip warning: name not matched: obj/foo.o
        zip warning: name not matched: obj/bar.o
        zip warning: name not matched: obj/baz.o

zip error: Nothing to do! (out.exe)
Makefile:9: recipe for target 'out.exe' failed
make: *** [out.exe] Error 12

It seems make wants to go a bit too fast by executing a recipe where its dependencies are not already made (obj/foo.o ...). Actually I was expecting an error like: "Unable to find VILLAIN to make obj/foo.o"

The Makefile:

#!/usr/bin/env make
SRCDIR = src
OBJDIR = obj

SRC = $(addsuffix .c,foo bar baz)
OBJ = $(addprefix $(OBJDIR)/, $(notdir $(SRC:c=o)))

out.exe: $(OBJ)
    zip $@ $^ 

$(OBJDIR)/%.o: $(SRCDIR)/%.c VILLAIN
    cp $< $@

$(OBJ) : | $(OBJDIR)

$(OBJDIR):
    mkdir -p $@

clean:
    rm -f *.c
    rm -rf $(OBJDIR)/ $(SRCDIR)/
    mkdir -p $(SRCDIR)/
    touch $(addprefix $(SRCDIR)/,$(SRC))

However if I remove the villain everything works fine:

$ make clean
rm -f *.c
rm -rf obj/ src/
mkdir -p src/
touch src/foo.c src/bar.c src/baz.c

$ make
mkdir -p obj
cp src/foo.c obj/foo.o
cp src/bar.c obj/bar.o
cp src/baz.c obj/baz.o
zip out.exe obj/foo.o obj/bar.o obj/baz.o
  adding: obj/foo.o (stored 0%)
  adding: obj/bar.o (stored 0%)
  adding: obj/baz.o (stored 0%)

Why make try to make a target before building its prerequisite?

Upvotes: 1

Views: 488

Answers (1)

MadScientist
MadScientist

Reputation: 100781

Pattern rules don't work like explicit rules in this respect. There can be many, many different pattern rules that could be used to create the same target (consider, a .o can be created from a C source file, C++ source file, FORTRAN source file, etc. etc.)

So, when make tries to find a pattern rule to build a target, in order to decide whether the pattern matches or not make will try to build all the prerequisites. If one of the prerequisites cannot be built, then it is not an error! Make simply goes on to the next pattern rule that matches and tries that. So the fact that VILLIAN doesn't exist and can't be built, just means that make will never select this pattern rule because the prerequisites cannot be satisfied... but this is not an error and no message will be printed (well, if you look at make's debug output you'll see a note about it).

So make will discover it has no rules that know how to build obj/foo.o. You'd expect to get an error at that point... but you won't because you've added this rule:

$(OBJ) : | $(OBJDIR)

By doing this you've declared obj/foo.o to be a target that make knows about and so it won't complain if the target doesn't exist. If you change this to add the order-only prerequisite into the pattern rule, then you'll get more comprehensible behavior:

$(OBJDIR)/%.o: $(SRCDIR)/%.c VILLAIN | $(OBJDIR)
        cp $< $@

$(OBJDIR):
        mkdir -p $@

Gives:

make: *** No rule to make target 'obj/foo.o', needed by 'out.exe'.  Stop.

Upvotes: 2

Related Questions