Make将删除文件作为中间文件



当试图将编译输出组织到构建目录中时,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视为一个中间文件。然而,我预计build/obj/test-mpi.o不会被视为中间文件,因为它显式地出现在目标$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c中,并且gnu使文档声明";通常,如果在makefile中提到文件是目标或先决条件,则该文件不能是中间文件;。

类似的问题中也报道过这种行为,但我认为在这两种情况下,文件都不应被视为中间文件,因为它们出现在目标中。我是不是错过了什么?

我认为make将build/obj/test mpi.o视为一个中间文件。

是的,看起来不错。

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

假定$(OBJ_DIR)扩展到build/obj,则模式$(OBJ_DIR)/%.obuild/obj/test-mpi.o匹配。这与build/obj/test-mpi.o显式出现相反。

即便如此,您已经正确阅读了GNUmake文档:如果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的内置隐式规则没有区别。

然而,尽管像您询问的文件这样的文件肯定是GNUmake定义的中间文件,但make在这里有一个额外的功能,可以满足您的目的。如果你想使用一个模式来指定你想要保留的中间目标,那么你可以通过将该模式指定为特殊目标.PRECIOUS的先决条件来实现,比如:

.PRECIOUS: $(OBJ_DIR)/%.o

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

我遇到了与@gkaf完全相同的问题,并发现这很疯狂:make删除了一个对象文件(是Makefile中提到的,尽管是隐含的),有效地违背了make的目的(避免重新编译)。。。感谢@John Bollinger澄清隐式/显式的区别,这就是make的预期行为。

.PRECIOUS更好的解决方案可能是.NOTINTERMEDIATE,如

.NOTINTERMEDIATE: $(OBJS)

如本文所述(您必须首先构造变量$(OBJS))。

另一种选择是完全避免模式规则(这会带来其他陷阱),支持静态模式规则。类似于:

$(OBJS) : $(OBJ_DIR)/%.o: $(SRC_DIR)/%.c ; ...

现在,这是通过$(OBJS)(我希望!)对对象文件的显式引用,因此它不受问题的影响。再次,价格是$(OBJS)定义的需要。

注意,关于静态模式与模式的文档似乎没有提到这些点(至少没有明确地提到;)

最新更新