user237419
user237419

Reputation: 9064

GNU make VPATH not properly used to rewrite object paths in rules

my Makefile:

CC=cc
INC=-I.
CFLAGS=-g  -DDEBUG -std=gnu99 -DSFHASH -DDOMAIN
LDFLAGS=-ldl
OBJS=*.o
OBJDIR=.obj
BINDIR=.obj
LIBDIR=.obj

%.o: %.c
        $(CC) $(INC) $(CFLAGS) -DSFHASH -o $(OBJDIR)/$@ -c $<


VPATH = .obj
#vpath %.o $(OBJDIR)

hashtest: hashfuncs.o hast.o scanners.o parseargs.o
        $(CC) -rdynamic -o $(BINDIR)/$@ $^ $(LDFLAGS)

clean:
        @rm -f $(OBJDIR)/$(OBJS) $(BINDIR)/hashtest

using it for the first time: make does not rewrite gcc's input file paths:

@delphi# make clean
@delphi# make
cc -I. -g  -DDEBUG -std=gnu99 -DSFHASH -DDOMAIN -DSFHASH -o .obj/hashfuncs.o -c hashfuncs.c
cc -I. -g  -DDEBUG -std=gnu99 -DSFHASH -DDOMAIN -DSFHASH -o .obj/hast.o -c hast.c
cc -I. -g  -DDEBUG -std=gnu99 -DSFHASH -DDOMAIN -DSFHASH -o .obj/scanners.o -c scanners.c
cc -I. -g  -DDEBUG -std=gnu99 -DSFHASH -DDOMAIN -DSFHASH -o .obj/parseargs.o -c parseargs.c
cc -rdynamic -o .obj/hashtest hashfuncs.o hast.o scanners.o parseargs.o -ldl
cc: error: hashfuncs.o: No such file or directory
cc: error: hast.o: No such file or directory
cc: error: scanners.o: No such file or directory
cc: error: parseargs.o: No such file or directory
make: *** [hashtest] Error 1

using it second time in a row, it does:

@delphi# make
cc -rdynamic -o .obj/hashtest .obj/hashfuncs.o .obj/hast.o .obj/scanners.o .obj/parseargs.o -ldl
@delphi# 

GNU Make manual says:

The value of the make variable VPATH specifies a list of directories that make should search. Most often, the directories are expected to contain prerequisite files that are not in the current directory; however, make uses VPATH as a search list for both prerequisites and *targets of rules*.

I would understand if VPATH is a set-once-at-startup-typeof-variable but it's not. The fact that the target's dependencies generated at runtime are properly looked up says so.

What's up with that? Why is make correctly rewriting input file paths for gcc only the second time?

Upvotes: 1

Views: 1706

Answers (2)

user237419
user237419

Reputation: 9064

Subject: Re: (un)expected make behavior while using VPATH
From: mad scientist

<<< %.o: %.c

<<< $(CC) $(INC) $(CFLAGS) -DSFHASH -o $(OBJDIR)/$@ -c $<

This is an invalid rule. It violates one of my "rules of makefiles":

http://make.mad-scientist.net/rules.html

<<< using it for the first time: make does not rewrite gcc's input file paths:

<<< using it second time in a row, it does:

Classic. Don't feel bad, that's what EVERYONE does at first. That may well point out that VPATH is mis-designed (or that some other facility is needed). However, that's another conversation. This might help:

http://make.mad-scientist.net/vpath.html

Cheers!

from help-make ml, thread http://lists.gnu.org/archive/html/help-make/2012-11/msg00018.html

Upvotes: 2

Reinier Torenbeek
Reinier Torenbeek

Reputation: 17383

The explanation of this behavior can be found in section 3.7 of the GNU Make manual:

3.7 How make Reads a Makefile

GNU make does its work in two distinct phases. During the first phase it reads all the makefiles, included makefiles, etc. and internalizes all the variables and their values, implicit and explicit rules, and constructs a dependency graph of all the targets and their prerequisites.

During the second phase, make uses these internal structures to determine what targets will need to be rebuilt and to invoke the rules necessary to do so.

When you run the make for the first time, the prerequisites in the .obj directory do not yet exist. That means that they will not be part of the dependency graph as created during the first phase. Consequently, these prerequisites are not used during the second phase when executing the rule to create hashtest -- even though they exist by that time due to rules previously executed during the second phase.

When you run make for the second time, these prerequisites do exist and are found and included in the dependency graph by virtue of the VPATH setting.

Please note that the expression in VPATH is only a search path, nothing more. It can contain multiple directory names, separated by a colon. make will search in that search path for prerequisites and targets while constructing the dependency graph during the first phase. If it finds a particular target in a directory in that search list, then that target is added to the dependency graph as an existing file, that might or might not be out of date.

You seem to expect that make adds a subdirectory from VPATH to a target, even if that target does not exist in that subdirectory and needs to be created. That is not how it works -- how would make deal with multiple directories in VPATH in that case?

Upvotes: 1

Related Questions