在咱们看别人写的有关项目的makefile文件时,常常会由于别人使用了makefile提供的自动推导规则或者自动变量,而致使只知道项目编译的大体流程,对其中具体的实现和编译流程不熟悉。有的时候带着疑问上百度,发现有关的makefile的问题的回答都不是本身所要找的答案。下面本身给你们说说本身遇到的问题和一下关于自动推导的规则,以帮助你们更好的学习和解决问题。函数
问题1:%.o : %.c学习
%.o : %.c $(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
网上回答:把全部的[.c]文件都编译成[.o]文件ui
疑惑:一、 %.o : %.c是怎么把全部的[.c]文件都编译成[.o]文件,具体流程是什么?spa
二、是一步一步编译的,仍是一下所有将.c文件所有编译成.o文件?命令行
问题2:all : $(BIN)code
BIN = hello all : $(BIN) %.o:%.c $(CC) $(CFLAGS) -c $< -o $@
疑惑:一、all : $(BIN) 中的all依赖目标是hello,hello是怎么生成的呢?编译器
让咱们带着疑问去看看makefile的自动推导规则和自动化变量。自动化
所谓自动化变量,就是这种变量会把模式中所定义的一系列的文件自动地挨个取出,直至全部的符合模式的文件都取完了。这种自动化变量只应出如今规则的命令中编译
$@
表示规则中的目标文件集。在模式规则中,若是有多个目标,那么, “$@”就是匹配于
目标中模式定义的集合。注意,其目标是一个一个取出来的class
$<
依赖目标中的第一个目标名字。若是依赖目标是以模式(即“%” )定义的,那么“$<”
将是符合模式的一系列的文件集。注意,其是一个一个取出来的。
$^
全部的依赖目标的集合。以空格分隔。若是在依赖目标中有多个重复的,那个这个变量
会去除重复的依赖目标,只保留一份。
$+
这个变量很像“$^” ,也是全部依赖目标的集合。只是它不去除重复的依赖目标。
$%
仅当目标是函数库文件中,表示规则中的目标成员名。例如,若是一个目标是
“foo.a(bar.o)” ,那么, “$%”就是“bar.o” , “$@”就是“foo.a” 。若是目标不是函
数库文件(Unix 下是[.a],Windows 下是[.lib]) ,那么,其值为空。
$?
全部比目标新的依赖目标的集合。以空格分隔。
$*
这个变量表示目标模式中"%"及其以前的部分。若是目标是"dir/a.foo.b",而且目标的模式是"a.%.b",那么,"$*"的值就是"dir/a.foo"。这个变量对于构造有关联的文件名是比较有较。若是目标中没有模式的定义,那么"$*"也就不能被推导出,可是,若是目标文件的后缀是make所识别的,那么"$*"就是除了后缀的那一部分。例如:若是目标是"foo.c",由于".c"是make所能识别的后缀名,因此,"$*"的值就是"foo"。
使用makefile的隐含规则来生成你所须要的目标,你所须要作的就是不要写出这个目标的规则。而是让make会去自动推导产生这个目标的规则和命令,若是make能够自动推导生成这个目标的规则和命令,那么这个行为就是隐含规则的自动推导。
foo : foo.o bar.o cc –o foo foo.o bar.o $(CFLAGS) $(LDFLAGS) #makfile会根根隐含规则自动将foo.c bar.c编译生成foo.o bar.o。
make会在本身的“隐含规则”库中寻找能够用的规则,若是找到,那么就会使用。若是找不到,那么就会报错。在上面的那个例子中,make调用的隐含规则是,把[.o]的目标的依赖
文件置成[.c],并使用C的编译命令“cc –c $(CFLAGS) [.c]”来生成[.o]的目标。也就是
说,咱们彻底没有必要写下下面的两条规则:
foo.o : foo.c cc –c foo.c $(CFLAGS) bar.o : bar.c cc –c bar.c $(CFLAGS)
一、编译C程序的隐含规则。
“<n>;.o”的目标的依赖目标会自动推导为“<n>;.c”,而且其生成命令是“$(CC) –c$(CPPFLAGS) $(CFLAGS)”
二、编译C++程序的隐含规则。
“<n>;.o”的目标的依赖目标会自动推导为“<n>;.cc”或是“<n>;.C”,而且其生成命令是“$(CXX) –c $(CPPFLAGS) $(CFLAGS)”。(建议使用“.cc”做为C++源文件的后缀,而不是“.C”)
三、编译Pascal程序的隐含规则。
“<n>;.o”的目标的依赖目标会自动推导为“<n>;.p”,而且其生成命令是“$(PC) –c$(PFLAGS)”。
四、编译Fortran/Ratfor程序的隐含规则。
“<n>;.o”的目标的依赖目标会自动推导为“<n>;.r”或“<n>;.F”或“<n>;.f”,而且其生成命令是:
“.f” “$(FC) –c $(FFLAGS)”
“.F” “$(FC) –c $(FFLAGS) $(CPPFLAGS)”
“.f” “$(FC) –c $(FFLAGS) $(RFLAGS)”
五、预处理Fortran/Ratfor程序的隐含规则。
“<n>;.f”的目标的依赖目标会自动推导为“<n>;.r”或“<n>;.F”。这个规则只是转换Ratfor或有预处理的Fortran程序到一个标准的Fortran程序。其使用的命令是:
“.F” “$(FC) –F $(CPPFLAGS) $(FFLAGS)”
“.r” “$(FC) –F $(FFLAGS) $(RFLAGS)”
六、编译Modula-2程序的隐含规则。
“<n>;.sym”的目标的依赖目标会自动推导为“<n>;.def” ,而且其生成命令是: “$(M2C) $(M2FLAGS) $(DEFFLAGS)”。“<n.o>;” 的目标的依赖目标会自动推导为“<n>;.mod”,而且其生成命令是:“$(M2C) $(M2FLAGS) $(MODFLAGS)”。
七、汇编和汇编预处理的隐含规则。
“<n>;.o” 的目标的依赖目标会自动推导为“<n>;.s”,默认使用编译品“as”,而且其生成命令是: “$(AS) $(ASFLAGS)” 。 “<n>;.s”的目标的依赖目标会自动推导为 “<n>;.S” ,默认使用C预编译器“cpp”,而且其生成命令是:“$(AS) $(ASFLAGS)”。
八、连接Object文件的隐含规则。
“<n>;”目标依赖于“<n>;.o” ,经过运行C的编译器来运行连接程序生成(通常是 “ld” ) ,其生成命令是:“$(CC) $(LDFLAGS) <n>;.o $(LOADLIBES) $(LDLIBS)”。这个规则对于只有一个源文件的工程有效,同时也对多个Object文件(由不一样的源文件生成)的也有效。例如以下规则:
x : y.o z.o
而且“x.c”、“y.c”和“z.c”都存在时,隐含规则将执行以下命令:
cc -c x.c -o x.o
cc -c y.c -o y.o
cc -c z.c -o z.o
cc x.o y.o z.o -o x
rm -f x.o
rm -f y.o
rm -f z.o
若是没有一个源文件(如上例中的x.c)和你的目标名字(如上例中的x)相关联,那么,你最好写出本身的生成规则,否则,隐含规则会报错的。
九、Yacc C程序时的隐含规则。
“<n>;.c” 的依赖文件被自动推导为 “n.y” (Yacc生成的文件) , 其生成命令是: “$(YACC) $(YFALGS)”。(“Yacc”是一个语法分析器,关于其细节请查看相关资料)
十、Lex C程序时的隐含规则。
“<n>;.c”的依赖文件被自动推导为“n.l” (Lex生成的文件),其生成命令是: “$(LEX) $(LFALGS)”。(关于“Lex”的细节请查看相关资料)
十一、Lex Ratfor程序时的隐含规则。
“<n>;.r”的依赖文件被自动推导为“n.l” (Lex生成的文件),其生成命令是: “$(LEX)
$(LFALGS)”。
十二、从C程序、Yacc文件或Lex文件建立Lint库的隐含规则。
“<n>;.ln” (lint生成的文件) 的依赖文件被自动推导为 “n.c” , 其生成命令是: “$(LINT) $(LINTFALGS) $(CPPFLAGS) -i”。对于“<n>;.y”和“<n>;.l”也是一样的规则。
在隐含规则中的命令中,基本上都是使用了一些预先设置的变量。你能够在你的makefile中改变这些变量的值,或是在make的命令行中传入这些值,或是在你的环境变量中设置这些
值,不管怎么样,只要设置了这些特定的变量,那么其就会对隐含规则起做用。固然,你也能够利用make的“-R”或“--no–builtin-variables”参数来取消你所定义的变量对隐含规则的做用。
一、关于命令的变量。
AR
函数库打包程序。默认命令是“ar”。
AS
汇编语言编译程序。默认命令是“as”。
CC
C语言编译程序。默认命令是“cc”。
CXX
C++语言编译程序。默认命令是“g++”。
CO
从 RCS文件中扩展文件程序。默认命令是“co”。
CPP
C程序的预处理器(输出是标准输出设备)。默认命令是“$(CC) –E”。
RM
删除文件命令。默认命令是“rm –f”。
二、关于命令参数的变量
若是没有指明其默认值,那么其默认值都是空。
ARFLAGS
函数库打包程序AR命令的参数。默认值是“rv”。
ASFLAGS
汇编语言编译器参数。(当明显地调用“.s”或“.S”文件时)。
CFLAGS
C语言编译器参数。
CXXFLAGS
C++语言编译器参数。
COFLAGS
RCS命令参数。
CPPFLAGS
C预处理器参数。( C 和 Fortran 编译器也会用到)。
LDFLAGS
连接器参数。(如:“ld”)
隐含规则链
例如,一个[.o]的文件生成,可能会是先被Yacc的[.y]文件先成[.c],而后再被C的编译器生成。咱们把这一系列的隐含规则叫作“隐含规则链”。
中间目标:咱们把这种[.c]的文件(或是目标),叫作中间目标。无论怎么样,make会努力自动推导生成目标的一切方法,无论中间目标有多少,其都会执着地把全部的隐含规则和你书写的规则所有合起来分析,努力达到目标
中间目标不一样点:在默认状况下,对于中间目标,它和通常的目标有两个地方所不一样:第一个不一样是除非中间的目标不存在,才会引起中间规则。第二个不一样的是,只要目标成功产生,那么,产生最终目标过程当中,所产生的中间目标文件会被以“rm -f”删除
强制声明中间目标:一般,一个被makefile指定成目标或是依赖目标的文件不能被看成中介。然而,你能够明显地说明一个文件或是目标是中介目标,你可使用伪目标“.INTERMEDIATE”来强制声明。(如:.INTERMEDIATE : mid )
阻止make自动删除中间目标:你可使用伪目标“.SECONDARY”来强制声明(如:.SECONDARY : sec)。你还能够把你的目标,以模式的方式来指定(如:%.o)成伪目标“.PRECIOUS”的依赖目标,以保存被隐含规则所生成的中间文件。
最后,相信你们应该都知道了上面问题的答案了吧。
问题1:%.o : %.c的生成流程?
makefile会利用本身的隐含规则和自动化变量,依次取出.o的目标文件,而后将这个.o依赖的.c文件找出来,编译成.o文件
问题2: all : $(BIN) 中的all依赖目标是hello,hello是怎么生成的呢?
首先,makefile会找到hello的依赖文件hello.o
而后,makefile会找到hello..o的依赖文件hello.c
最后,将hello.c编译成hello..o,再编译成hello文件