The Basics: VPATH and vpath



SRCS := foo.c bar.c baz.c

OBJS := $(SRCS:.c=.o)

.PHONY: debug release

debug: all

debug: CPPFLAGS += -g

release: all

all: $(OBJS)

foo.o: foo_header.h

bar.o: string.h

vpath %.h /usr/include

vpath %.c ../src





The mode is controlled by setting the DEBUG variable on the command-line to yes or no.  If DEBUG is set then the first part of the Makefile is used (and it only contains a single all rule).  The first part first translates the setting of DEBUG into either debug or release and then executes a sub-Make in the appropriate directory.

For example, if you do make DEBUG=yes then the Makefile will


make debug -C debug -f ../Makefile DEBUG= That starts GNU Make in the debug sub-directory (that's the -C debug > part), using the same Makefile (the -f ../Makefile ; the .. is necessary because this will be executed from debug/), clears the DEBUG variable and tells the sub-Make to run the debug rule.

When the Makefile is rerun the first part is ignored (because DEBUG was cleared) and the main part runs.  In this case the debug target is built.  It modifies CPPFLAGS and builds all.

Here's what happens when you do a debug build:

$ make DEBUG=yes

make debug -C debug -f ../Makefile DEBUG= cc  -I ../include -g  -c -o foo.o ../src/foo.c cc  -I ../include -g  -c -o bar.o ../src/bar.c cc  -I ../include -g  -c -o baz.o ../src/baz.c And a release build:

$ make DEBUG=no

make release -C release -f ../Makefile DEBUG= cc  -I ../include  -c -o foo.o ../src/foo.c cc  -I ../include  -c -o bar.o ../src/bar.c cc  -I ../include  -c -o baz.o ../src/baz.c In both cases the output has been separated into a directory with the sources coming from the same location.  For the debug build you can see that the -g option has been added to the compiler command-line.

<size=12pt>You don't need them

Most of the time you can completely avoid using VPATH and vpath.  For most dependencies you'll be using some automatic dependency search program (such as makedepend or gcc

- -MP ) and so you won't need to have GNU Make search for those files because the dependencies will be fully specified.

On the other hand, the separation of different binary types (debug/release, or different architectures) is very handy.  It can, however, be easily achieved without using the VPATH/vpath by explicitly setting the paths for sources and objects in the Makefile.  The only problem with doing this is that GNU Make's built-in pattern rules cannot be used (because they either assume source and binary are in the same directory, or they use a

VPATH/vpath search).   To make up for that you have to provide your own versions of those pattern rules.

Here's my sample Makefile rewritten to eliminate vpath entirely (I cheated slightly be omitting the automatic dependency generation part).  It still separates output into debug/ and release/ but uses explicit paths to do so, and it doesn't require the GNU Make restart that the previous solution used.

OBJDIR := $(if $(filter yes,$(DEBUG)),debug,release) SRCDIR := src



SRCS := foo.c bar.c baz.c

OBJS := $(addprefix $(OBJDIR)/,$(SRCS:.c=.o)) SRCS := $(addprefix $(SRCDIR)/,$(SRCS))

all: $(OBJS)

ifeq ($(OBJDIR),debug)

all: CPPFLAGS += -g


$(OBJDIR)/%.o: $(SRCDIR)/%.c


If you don't know the commands used by a built-in rule you can type make -p to get GNU Make to print them out.  For this rewrite I used the definition of the %.o: %.c rule:

%.o : %.c


About the author

AgileConnection is a TechWell community.

Through conferences, training, consulting, and online resources, TechWell helps you develop and deliver great software every day.