How to change current directory in GNU Make
The correct way is to open a man page:
man make
and search for -C
by inputting /-C
and pressing Enter once. You will find something like this:
-C dir, --directory=dir
Change to directory dir before reading the makefiles or doing
anything else. If multiple -C options are specified, each is
interpreted relative to the previous one: -C / -C etc is equiv‐
alent to -C /etc. This is typically used with recursive invo‐
cations of make.
So you can use it:
make -C <desired directory> ...
Building targets in a separate directory is a commonplace make
practice
that GNU make
conveniently supports without changing directory or
invoking auxiliary tools. Here is a routine illustration:
Makefile
srcs := main.c foo.c
blddir := bld
objs := $(addprefix $(blddir)/,$(srcs:.c=.o))
exe := $(blddir)/prog
.PHONY: all clean
all: $(exe)
$(blddir):
mkdir -p $@
$(blddir)/%.o: %.c | $(blddir)
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
$(exe) : $(objs)
$(CC) -o $@ $^ $(LDFLAGS) $(LDLIBS)
clean:
rm -fr $(blddir)
which runs like:
$ make
mkdir -p bld
cc -c -o bld/main.o main.c
cc -c -o bld/foo.o foo.c
cc -o bld/prog bld/main.o bld/foo.o
Cribs:-
$(addprefix $(blddir)/,$(srcs:.c=.o))
- 8.2 Functions for String Substitution and Analysis
- 6.3.1 Substitution References
$(blddir)/%.o: %.c | $(blddir)
- 10.5 Defining and Redefining Pattern Rules
- 4.3 Types of Prerequisites
There can be powerful reasons to make make
change its working directory but
merely putting build products in a separate directory isn't one.
Known methods overview
The excellent research of various methods how to separate the source and target directories was made by Paul D. Smith in "Multi-Architecture Builds" paper. The following methods are described (with their drawbacks):
- Source copy
- Explicit path (reference to every target is prefixed with the pathname)
- VPATH (invoke build from the target directory)
- Advanced VPATH (auto recursive invocation)
Yet another method
However I found the simpler solution — with smaller boilerplate and without recursive invocation of make
. In case of GNU Make with Guile support we can just use Guile chdir
function to change the current working directory from Makefile.
Also we can create directory via mkdir
before that.
data ?= ./data/
# Create $(data) directory if it is not exist (just for example)
$(guile (if (not (access? "$(data)" F_OK)) (mkdir "$(data)") ))
# Set the new correct value of CURDIR (before changing directory)
CURDIR := $(abspath $(data))
# Change the current directory to $(data)
$(guile (chdir "$(data)"))
# Another way of updating CURDIR
# — via sub-shell call after changing directory
# CURDIR := $(shell pwd)
# Don't try to recreate Makefile file
# that is disappeared now from the current directory
Makefile : ;
$(info CURDIR = $(CURDIR) )
$(info PWD = $(shell pwd) )
Final boilerplate to change the current directory
The assumptions: data
variable is available in the context and the parent of $(data)
directory is accessible, the path can be relative.
srcdir := $(realpath $(dir $(lastword $(MAKEFILE_LIST))))
ifeq (,$(filter guile,$(.FEATURES)))
$(warning Guile is required to change the current directory.)
$(error Your Make version $(MAKE_VERSION) is not built with support for Guile)
endif
$(MAKEFILE_LIST): ;
$(guile (if (not (file-exists? "$(data)")) (mkdir "$(data)") ))
ORIGCURDIR := $(CURDIR)
CURDIR := $(realpath $(data))
$(guile (chdir "$(data)"))
ifneq ($(CURDIR),$(realpath .))
$(error Cannot change the current directory)
endif
$(warning CURDIR is changed to "$(data)")
Remember that relative path in include
directive is calculated from the current directory by default, hence it depends on the location — is it used before this boilerplate or after.
NB: $(data)
should not be used in the rules; $(srcdir)
can be used to specify a file relative to this Makefile file location.
Found issues
This method was tested in GNU Make 4.0 and 4.2.1
One minor issue was observed. abspath
function works incorrectly after changing the current directory — it continues resolving relative paths according to the old CURDIR
; realpath
works correctly.
Also this method may have other yet unknown drawbacks.
In the GNU Make program I am using built for mingw64 (windows),
GNU Make 4.2.1 Built for x86_64-w64-mingw32
I am able to use this target with this command,
debug:
cd $(PREFIX) && $(GDB) kiwigb.exe
The results are the directory change is temporary, but all works.