当试图将编译输出组织到构建目录中时,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
之后被删除。
我认为make
将build/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)/%.o
与build/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)
定义的需要。
注意,关于静态模式与模式的文档似乎没有提到这些点(至少没有明确地提到;)