我有一个非常简单的makefile,如下所示:
.PHONY: clean all
CC = /home/utils/gcc-5.2.0/bin/g++
CFLAGS = -Wall -Werror -fPIC
SRC = $(wildcard *.c)
OBJ = $(subst .c,.o,$(SRC))
.INTERMEDIATE: $(OBJ)
all: test.so
%.o: %.c
$(CC) $(CFLAGS) -o $@ -c $<
test.so: $(OBJ)
$(CC) -shared $^ -o $@
clean:
@rm -f *.o *~ *.so
我在同一目录中只有两个文件:a.c和b.c当我执行"make all"时,我得到了以下内容,这是完美的。
/home/utils/gcc-5.2.0/bin/g++ -Wall -Werror -fPIC -o a.o -c a.c
/home/utils/gcc-5.2.0/bin/g++ -Wall -Werror -fPIC -o b.o -c b.c
/home/utils/gcc-5.2.0/bin/g++ -shared a.o b.o -o test.so
rm a.o b.o
但是,如果我这样做:触摸交流;使所有
我得到了与上面相同的make执行序列,这不是我所期望的。a.c和b.c之间没有依赖关系。我期望的是:
/home/utils/gcc-5.2.0/bin/g++ -Wall -Werror -fPIC -o a.o -c a.c
/home/utils/gcc-5.2.0/bin/g++ -shared a.o b.o -o test.so
rm a.o
我不明白为什么要重新编译b.c。根据gnumake手册:
第一个区别是如果中间文件不存在会发生什么。如果一个普通文件b不存在,并且make考虑了一个依赖于b的目标,那么它总是创建b,然后从b更新目标。但如果b是一个中间文件,那么make可以很好地保留它。它不会麻烦更新b或最终目标,除非b的某些先决条件比该目标更新,或者有其他原因更新该目标。
b.o是一个中间文件,不应该再次编译,因为b.c没有更改。我错过了什么?
如果未重新编译b.o
,则无法创建test.so
,因为它依赖于b.o
。如果删除了b.o
,则无法从a.o
和b.o
创建共享库。
如果您试图创建一个静态库,那么您可以使用make的特殊归档语法来替换a.o
,而无需重新编译b.o
,但除非您使用归档特殊语法,否则这是不可能的,而且对于共享库(如您试图在此处构建的库),这是不可行的。
默认情况下,这些.o
文件不被视为中间文件是有原因的,通过添加.INTERMEDIATE
目标来强制它们并不意味着可以避免重建它们。
您的困难源于您所说的中间文件并不是GNUMake的意思;但.INTERMEDIATE
的特殊目标允许你坚持a.o
和b.o
,它们在您的makefile中不是GNU Make的中间文件意义,应被视为它们是GNU Make's sense中的中间,并且那么后果会让你大吃一惊。
您所说的F是一个中间文件的意思是:F是目标T的先决条件,并且F本身具有先决条件。因此CCD_ 14和CCD_test.so
,因为它们是该目标的先决条件,并且它们本身具有各自的先决条件a.c
和b.c`.
在中间的意义上,使目标test.so
不需要(a.o
|b.o
),除非它早于(a.c|b.c
)。所以你被这个事实吓了一跳当a.o
和b.o
被设置为.INTERMEDIATE
时,GNU Make在设置test.so
:时总是删除它们
b.o是一个中间文件,不应该再次编译,因为b.c没有更改。我错过了什么?
您主要错过了10.4条隐式规则链
通常,如果在生成文件中提到文件是目标或先决条件,则该文件不能是中间文件。但是,您可以通过将文件列为特殊目标.intermediate的先决条件,将其明确标记为中间文件。即使以其他方式明确提及该文件,这也会生效。
因此,a.o
和b.o
在GNU Make的意义上通常不会是中间的,因为它们在您的makefile中被提及为test.so
的前提条件。但你可以在GNUMake的意义上,通过将它们列为.INTERMEDIATE
的先决条件,使它们被视为中间体——就像您所做的那样。
GNU Make所说的中间比你的意思更具技术性在手册的同一节中进行了解释:
有时一个文件可以由一系列隐式规则组成。例如,文件n.o可以由n.y通过先运行Yacc然后运行cc来生成。这样的序列被称为链。
如果文件n.c存在,或者在makefile中提到,则不需要进行特殊搜索:make发现对象文件可以从n.C通过C编译生成;稍后,当考虑到如何生成n.c,使用了运行Yacc的规则。最终,n.c和n.o都会更新。
然而,即使n.c不存在,也没有被提及,make也知道如何将其设想为n.o和n.y之间缺少链接在这种情况下,n.c被称为中间文件。一旦make决定使用中间文件,它就会作为如果在makefile中提到了它,以及说明如何创建它的隐含规则
(我的重点)。更简单地说,如果Make需要使目标输出,并发现文件输入即:
- 它没有明确的规则使输出来自输入,但是-
- 它通过以下方式知道一系列隐式(也称为内置)规则不是makefile中的目标或先决条件的文件阶段可以从输入中生成,并从中生成输出,然后它将采用使输出的规则序列从通过阶段输入,并且将是中间文件
至关重要的是,由于阶段不是您的目标之一,也不是其中任何一个的先决条件,因此知道它仅仅是从输入产生输出的一次性副产品,并且因此可以在其已经达到该目的时被删除。
这里有一个简单的示例项目,涉及GNUMake意义上真正中间的文件。我们在项目目录中有一些yacc
源yacc.y
和lex
源lex.l
,我们想用它们构建一个解析器parse
,并使用以下makefile:
生成文件
YFLAGS := -d
OBJS := yacc.o lex.o
.PHONY: all clean
all: parse
parse: $(OBJS)
$(CC) $^ -o $@
clean:
$(RM) parse $(OBJS)
它的运行方式如下:
$ make
yacc -d yacc.y
mv -f y.tab.c yacc.c
cc -c -o yacc.o yacc.c
lex -t lex.l > lex.c
cc -c -o lex.o lex.c
cc yacc.o lex.o -o parse
rm lex.c yacc.c
现在你可以看到,Make使用其隐含规则目录可以通过编译不存在的C源文件来生成yacc.o
yacc.c
,并且yacc.c
可以从现有的源文件生成通过运行CCD_ 39来执行CCD_。同样,它发现lex.o
可以通过编译不存在的C源lex.c
和lex.c
可以通过运行CCD_ 44由CCD_。则parse
为由yacc.o
和lex.o
通过makefile指定。
但是您的makefile中没有任何关于yacc.c
和lex.c
的内容。远至你已经告诉Make,这样的文件是否存在对你来说并不重要。它们是中间文件,只是.y -> .o
和.l -> .o
产品中的阶段。所以当Make完成时:
rm lex.c yacc.c
如果你:
$ touch yacc.y
再次:
$ make
yacc -d yacc.y
mv -f y.tab.c yacc.c
cc -c -o yacc.o yacc.c
cc yacc.o lex.o -o parse
rm yacc.c
对于parse
的链接,只有yacc.o
被重新制作,并且删除了这次生成的一个中间文件yacc.c
。
底线
你的对象文件不是GNU意义上的中间文件,你也没有希望他们被当作自己一样对待。这对于对象文件来说是正常的。因此,删除.INTERMEDIATE: $(OBJ)
。