11

我想将带有源的目录与带有目标的目录分开。似乎从 Makefile 更改当前工作目录应该是最简单的解决方案。

由于以下缺点,目标的显式路径是不够的:

  1. Makefile 中的冗余代码,因为对目标的每个引用都应以变量为前缀。
  2. 用于构建特定中间目标的更复杂的命令行(更不利于调试)。

另见Pauls 的规则 #3

如果目标构建在当前工作目录中,那么生活是最简单的。

关于 VPATH——我也同意要求开发人员“在运行 make 之前更改到目标目录是一种痛苦”。

4

4 回答 4

8

在单独的目录中构建目标是makeGNU 方便地支持的一种常见做法,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

婴儿床:-

更改其工作目录可能有充分的理由,make但仅仅将构建产品放在单独的目录中并不是一个原因。

于 2016-05-26T19:31:38.893 回答
7

在我为 mingw64 (windows) 构建的 GNU Make 程序中,

GNU Make 4.2.1 为 x86_64-w64-mingw32 构建

我可以通过这个命令使用这个目标,

debug:
    cd $(PREFIX) && $(GDB) kiwigb.exe

结果是目录更改是暂时的,但一切正常。

于 2019-12-04T09:26:10.417 回答
5

已知方法概述

Paul D. Smith 在“ Multi-Architecture Builds ”一文中对如何分离源目录和目标目录的各种方法进行了出色的研究。描述了以下方法(及其缺点):

  • 源副本
  • 显式路径(对每个目标的引用都以路径名为前缀)
  • VPATH(从目标目录调用构建)
  • 高级 VPATH(自动递归调用)

还有一种方法

然而,我找到了更简单的解决方案——使用更小的样板并且没有递归调用make. 如果 GNU Make支持 Guile ,我们可以使用 Guilechdir函数从 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更改当前目录后功能无法正常工作 - 它继续根据旧的解析相对路径CURDIRrealpath工作正常。

此外,这种方法可能还有其他未知的缺点。

于 2016-05-26T17:57:45.400 回答
2

正确的方法是打开手册页:

man make

-C 输入/-C 并按Enter一次搜索。你会发现这样的东西:

   -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> ...
于 2021-05-04T13:00:28.897 回答