如何在 GNU Make 中更改当前目录
How to change current directory in GNU Make
我想将包含源的目录与包含目标的目录分开。而且从 Makefile 中更改当前工作目录似乎应该是最简单的解决方案。
由于以下缺点,目标的显式路径是不够的:
- Makefile 中的冗余代码,因为每个对目标的引用都应以变量为前缀。
- 更复杂的命令行来构建特定的中间目标(更不利于调试)。
另见 Pauls's rule #3:
Life is simplest if the targets are built in the current working
directory.
Regarding VPATH — 我也同意要求开发人员 "to change to the target directory before running make is a pain"。
已知方法概述
Paul D. Smith 在“Multi-Architecture Builds”论文中对如何分离源目录和目标目录的各种方法进行了出色的研究。描述了以下方法(及其缺点):
- 来源副本
- 显式路径(对每个目标的引用都带有路径名前缀)
- VPATH(从目标目录调用构建)
- 高级 VPATH(自动递归调用)
又一个方法
但是我找到了更简单的解决方案——使用更小的样板文件并且没有 make
的递归调用。在 GNU Make 的情况下,使用 Guile support we can just use Guile chdir
function 从 Makefile 更改当前工作目录。
我们也可以在此之前通过 mkdir
创建目录。
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) )
更改当前目录的最终样板文件
假设:data
变量在上下文中可用并且 $(data)
目录的父目录可访问,路径可以是相对的。
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)")
请记住,include
指令中的相对路径默认是从当前目录计算的,因此它取决于位置——它是在该样板文件之前还是之后使用。
注意:$(data)
不应该在规则中使用; $(srcdir)
可用于指定相对于此 Makefile 文件位置的文件。
发现问题
此方法已在 GNU Make 4.0 和 4.2.1 中测试
观察到一个小问题。 abspath
function works incorrectly after changing the current directory — it continues resolving relative paths according to the old CURDIR
; realpath
工作正常。
此外,此方法可能还有其他未知的缺点。
在单独的目录中构建目标是一种常见的做法 make
GNU make
方便地支持而无需更改目录或
调用辅助工具。下面是例程说明:
生成文件
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)
运行方式如下:
$ 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
婴儿床:-
$(addprefix $(blddir)/,$(srcs:.c=.o))
$(blddir)/%.o: %.c | $(blddir)
可能有充分的理由让 make
更改其工作目录,但是
仅仅将构建产品放在一个单独的目录中不是一个。
在我使用的为 mingw64 构建的 GNU Make 程序中 (windows),
GNU Make 4.2.1 Built for x86_64-w64-mingw32
我可以通过这个命令使用这个目标,
debug:
cd $(PREFIX) && $(GDB) kiwigb.exe
结果是目录更改是临时的,但一切正常。
正确的方法是打开手册页:
man make
并通过输入 /-C
并按一次 Enter 来搜索 -C
。你会发现这样的东西:
-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.
所以你可以使用它:
make -C <desired directory> ...
我想将包含源的目录与包含目标的目录分开。而且从 Makefile 中更改当前工作目录似乎应该是最简单的解决方案。
由于以下缺点,目标的显式路径是不够的:
- Makefile 中的冗余代码,因为每个对目标的引用都应以变量为前缀。
- 更复杂的命令行来构建特定的中间目标(更不利于调试)。
另见 Pauls's rule #3:
Life is simplest if the targets are built in the current working directory.
Regarding VPATH — 我也同意要求开发人员 "to change to the target directory before running make is a pain"。
已知方法概述
Paul D. Smith 在“Multi-Architecture Builds”论文中对如何分离源目录和目标目录的各种方法进行了出色的研究。描述了以下方法(及其缺点):
- 来源副本
- 显式路径(对每个目标的引用都带有路径名前缀)
- VPATH(从目标目录调用构建)
- 高级 VPATH(自动递归调用)
又一个方法
但是我找到了更简单的解决方案——使用更小的样板文件并且没有 make
的递归调用。在 GNU Make 的情况下,使用 Guile support we can just use Guile chdir
function 从 Makefile 更改当前工作目录。
我们也可以在此之前通过 mkdir
创建目录。
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) )
更改当前目录的最终样板文件
假设:data
变量在上下文中可用并且 $(data)
目录的父目录可访问,路径可以是相对的。
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)")
请记住,include
指令中的相对路径默认是从当前目录计算的,因此它取决于位置——它是在该样板文件之前还是之后使用。
注意:$(data)
不应该在规则中使用; $(srcdir)
可用于指定相对于此 Makefile 文件位置的文件。
发现问题
此方法已在 GNU Make 4.0 和 4.2.1 中测试
观察到一个小问题。 abspath
function works incorrectly after changing the current directory — it continues resolving relative paths according to the old CURDIR
; realpath
工作正常。
此外,此方法可能还有其他未知的缺点。
在单独的目录中构建目标是一种常见的做法 make
GNU make
方便地支持而无需更改目录或
调用辅助工具。下面是例程说明:
生成文件
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)
运行方式如下:
$ 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
婴儿床:-
$(addprefix $(blddir)/,$(srcs:.c=.o))
$(blddir)/%.o: %.c | $(blddir)
可能有充分的理由让 make
更改其工作目录,但是
仅仅将构建产品放在一个单独的目录中不是一个。
在我使用的为 mingw64 构建的 GNU Make 程序中 (windows),
GNU Make 4.2.1 Built for x86_64-w64-mingw32
我可以通过这个命令使用这个目标,
debug:
cd $(PREFIX) && $(GDB) kiwigb.exe
结果是目录更改是临时的,但一切正常。
正确的方法是打开手册页:
man make
并通过输入 /-C
并按一次 Enter 来搜索 -C
。你会发现这样的东西:
-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.
所以你可以使用它:
make -C <desired directory> ...