我想将源代码目录与目标文件目录分开。似乎从Makefile更改当前工作目录是最简单的解决方案。
显式指定目标路径存在以下缺点:
- 在Makefile中需要针对每个目标引用添加变量,导致代码冗余。
- 构建特定中间目标需要更复杂的命令行(不利于调试)。
请参阅Pauls的规则#3:
如果在当前工作目录中构建目标,则最简单。
关于VPATH——我也同意要求开发人员“在运行 make 命令之前切换到目标目录是很麻烦的”。
我想将源代码目录与目标文件目录分开。似乎从Makefile更改当前工作目录是最简单的解决方案。
显式指定目标路径存在以下缺点:
请参阅Pauls的规则#3:
如果在当前工作目录中构建目标,则最简单。
关于VPATH——我也同意要求开发人员“在运行 make 命令之前切换到目标目录是很麻烦的”。
将目标文件建立在单独的目录中是一种常见的make
实践,GNU make
可以方便地支持此操作而无需更改目录或调用辅助工具。以下是一个常规示例:
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)
这段代码的执行过程如下:
$ 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
的工作目录,但仅仅是将生成的文件放在另一个目录中并不是这样做的一个好理由。
我可以使用以下命令来运行此目标,GNU Make 4.2.1 Built for x86_64-w64-mingw32
debug:
cd $(PREFIX) && $(GDB) kiwigb.exe
结果是目录更改是暂时的,但一切正常。
从Makefile更改当前工作目录似乎是最简单的解决方案。- user2262111
Paul D. Smith 在 "多架构构建" 论文中对各种分离源目录和目标目录的方法进行了优秀的研究。以下方法被描述(包括它们的缺点):
make
。对于带有Guile支持的GNU Make,我们可以使用Guile chdir
函数来从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
指令中的相对路径默认是从当前目录计算的,因此它取决于位置——是在这个样板之前使用还是之后。
NB: 规则中不应使用$(data)
;可以使用$(srcdir)
来指定相对于此Makefile文件位置的文件。
此方法已在GNU Make 4.0和4.2.1中进行了测试。
观察到一个小问题。更改当前目录后,abspath
函数会错误地继续根据旧的CURDIR
解析相对路径;realpath
则正常工作。
此外,此方法可能存在其他尚未知的缺点。
正确的方法是打开一个 man 页面:
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> ...
-C
命令行选项指定目标目录与使用cd
或pushd
更改目录后再运行make
一样麻烦。而且你仍然需要将源Makefile放置到目标目录中。 - ruvimBUILD_DIR:=build
SRC_P1:=hello.c bye.c
HDR_P1:=hello.h
OBJ_P1:=$(addprefix $(BUILD_DIR)/,$(SRC_P1:.c=.o))
P1:=$(BUILD_DIR)/hello
CFLAGS=-I.
LDFLAGS=-L.
$(P1): $(BUILD_DIR) $(OBJ_P1)
$(LINK.c) $(OUTPUT_OPTION) $(OBJ_P1)
$(OBJ_P1): $(HDR_P1)
$(BUILD_DIR)/%.o: %.c
$(COMPILE.c) $(OUTPUT_OPTION) $<
$(BUILD_DIR):
mkdir $@
.PHONY: clean
clean:
rm -fr $(BUILD_DIR)
这与上面的答案非常相似,不同之处在于可以定义CFLAGS和LDFLAGS而无需在配方部分中显式包含它们。规则$(BUILD_DIR)/%.o: %.c
非常有趣,因为它仅适用于定义的对象集,而不是当前目录中的每个.c
文件。