【问题标题】:Make removes files as intermediateMake 删除文件为中间文件
【发布时间】:2021-10-09 23:13:35
【问题描述】:

当尝试将编译输出组织到构建目录中时,make 不断删除目标文件。 Makefile 是:

MPI_INSTALLATION=/home/gkaf/Software/MPI
IDIR=$(MPI_INSTALLATION)/include
LDIR=$(MPI_INSTALLATION)/lib

CC=gcc
CFLAGS=-I$(IDIR)
LDFLAGS=-L$(LDIR) -Wl,-rpath=$(LDIR)

BUILD_DIR=build
OBJ_DIR=$(BUILD_DIR)/obj
BIN_DIR=$(BUILD_DIR)/bin

SRC_DIR=src

LIBS=-lmpi

.PHONY: all
all: test-mpi

.PHONY: test-mpi
test-mpi: prepare $(BIN_DIR)/test-mpi

$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
        $(CC) -c -o $@ $< $(CFLAGS)

$(BIN_DIR)/%: $(OBJ_DIR)/%.o
        $(CC) -o $@ $^ $(LDFLAGS) $(LIBS)

prepare: $(BUILD_DIR) $(OBJ_DIR) $(BIN_DIR)

$(BUILD_DIR):
        mkdir -p $(BUILD_DIR)
$(OBJ_DIR):
        mkdir -p $(OBJ_DIR)
$(BIN_DIR):
        mkdir -p $(BIN_DIR)

.PHONY: clean
clean:
        rm -rf $(BUILD_DIR)

编译过程中生成的目标文件build/obj/test-mpi.o在创建可执行文件build/bin/test-mpi后被删除。

我相信makebuild/obj/test-mpi.o 视为intermediate file。但是,我期望 build/obj/test-mpi.o 不会被视为中间文件,因为它明确出现在目标 $(OBJ_DIR)/%.o: $(SRC_DIR)/%.c 中,并且 gnu make 文档指出“通常,如果文件在 makefile 中作为目标或先决条件”。

这种行为has been reported 在类似的问题中,但我相信在这两种情况下,文件不应被视为中间文件,因为它们出现在目标中。我错过了什么吗?

【问题讨论】:

  • build/obj/test-mpi.o 是如何显式出现在$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c 中的?
  • 问题是,你为什么关心目标文件消失了?
  • 我想维护目标文件以避免不必要的重新编译。在这种情况下,可执行文件依赖于单个目标文件,但如果以后的可执行文件依赖于多个目标文件,我想只重新编译有变化的目标文件。
  • 如果可执行文件依赖于多个目标文件,那么您必须在生成文件中编写一个规则来列出所有这些文件。一旦编写了这样的规则,目标文件将不再被视为中间文件。

标签: makefile


【解决方案1】:

我相信 make 将 build/obj/test-mpi.o 视为中间文件。

是的,看起来不错。

但是,我期望 build/obj/test-mpi.o 不会被视为中间文件,因为它明确出现在目标 $(OBJ_DIR)/%.o: $(SRC_DIR)/%.c [...]

鉴于$(OBJ_DIR) 扩展为build/obj模式 $(OBJ_DIR)/%.o 匹配 build/obj/test-mpi.o。这与显式出现的build/obj/test-mpi.o 正好相反。

即便如此,您已经正确阅读了 GNU make 文档:如果 make 被提及为某个其他规则的目标或先决条件,它不会将 build/obj/test-mpi.o 视为中间文件。但事实并非如此。如果make 完全构建了该文件,这完全是make 的想法,尽管您为它做出该决定做好了准备。这正是中间文件的含义。

我错过了什么吗?

显然,正如 GNU 文档所说,您错过了在 makefile 中“提及”文件的含义。这意味着在宏扩展之后,文件名在 makefile 文本中按字面意思出现,作为规则的目标或先决条件。示例:

$(BIN_DIR)/test-mpi: $(OBJ_DIR)/test-mpi.o

$(OBJ_DIR)/test-mpi.o: $(SRC_DIR)/test-mpi.c

匹配隐式(“模式”)规则的目标或先决条件模式是不够的。事实上,正是在make 旨在删除的隐式规则链中作为中间体生成的文件。 makefile 中定义的隐式规则在这方面与make 的内置隐式规则没有区别。

但是,尽管像您询问的那样的文件绝对是中间文件,因为 GNU make 定义了该术语,但 make 在这里有一个额外的功能,可能会满足您的目的。如果您想使用模式来指定要保留的中间目标,则可以通过将模式指定为特殊目标 .PRECIOUS 的先决条件来实现,如下所示:

.PRECIOUS: $(OBJ_DIR)/%.o

匹配这种模式的中间文件将不会被自动删除,否则它们将受到影响。

【讨论】:

  • 非常好的答案。提及.PRECIOUS GNU make 目标可能会有用。
  • 添加行 .PRECIOUS: $(OBJ_DIR)/%.o 确实可以防止 make 删除中间文件。我选择为每个可执行文件明确定义规则,因为它更灵活。例如,如果可执行文件test-mpi 还依赖于某个目标文件functions.o,我可以简单地将规则$(BIN_DIR)/test-mpi: $(OBJ_DIR)/test-mpi.o 扩展为$(BIN_DIR)/test-mpi: $(OBJ_DIR)/test-mpi.o $(OBJ_DIR)/functions.o
  • 谢谢你,@RenaudPacalet。我添加了对.PRECIOUS的相关属性的简要讨论。
  • 我绝对不推荐以这种方式使用.PRECIOUS。这不是它的目的,它有令人不快的副作用。特别是,如果编译器失败或您使用 ^C 中断构建,那么这些目标文件可能会以“较新”的时间戳留在磁盘上,因此当您重新运行 make 时它们不会被重建。
猜你喜欢
  • 2018-05-06
  • 1970-01-01
  • 1970-01-01
  • 2023-03-24
  • 1970-01-01
  • 2011-03-20
  • 1970-01-01
  • 2010-10-14
  • 1970-01-01
相关资源
最近更新 更多