makefile提高篇—自动推导和自动化变量

    在我们看别人写的有关项目的makefile文件时,经常会因为别人使用了makefile提供的自动推导规则或者自动变量,而导致只知道项目编译的大致流程,对其中具体的实现和编译流程不熟悉。有的时候带着疑问上百度,发现有关的makefile的问题的回答都不是自己所要找的答案。下面自己给大家说说自己遇到的问题和一下关于自动推导的规则,以帮助大家更好的学习和解决问题。

问题1:%.o : %.c

%.o : %.c
     $(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@

 网上回答:把所有的[.c]文件都编译成[.o]文件

疑惑:1、  %.o : %.c是怎么把所有的[.c]文件都编译成[.o]文件,具体流程是什么?

          2、是一步一步编译的,还是一下全部将.c文件全部编译成.o文件?

问题2:all : $(BIN)

BIN = hello
all : $(BIN)

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

疑惑:1、all : $(BIN) 中的all依赖目标是hello,hello是怎么生成的呢?

    让我们带着疑问去看看makefile的自动推导规则和自动化变量。

自动化变量

    所谓自动化变量,就是这种变量会把模式中所定义的一系列的文件自动地挨个取出直至所有的符合模式的文件都取完了。这种自动化变量只应出现在规则的命令中

$@
表示规则中的目标文件集。在模式规则中,如果有多个目标,那么, “$@”就是匹配于
目标中模式定义的集合。注意,其目标是一个一个取出来的

$<
依赖目标中的第一个目标名字。如果依赖目标是以模式(即“%” )定义的,那么“$<”
将是符合模式的一系列的文件集。注意,其是一个一个取出来的。

$^
所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的,那个这个变量
会去除重复的依赖目标,只保留一份。

$+
这个变量很像“$^” ,也是所有依赖目标的集合。只是它不去除重复的依赖目标。

$%
仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是
“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)
成用的隐含规则

1、编译C程序的隐含规则。
“<n>;.o”的目标的依赖目标会自动推导为“<n>;.c”,并且其生成命令是“$(CC) –c$(CPPFLAGS) $(CFLAGS)”

2、编译C++程序的隐含规则。
“<n>;.o”的目标的依赖目标会自动推导为“<n>;.cc”或是“<n>;.C”,并且其生成命令是“$(CXX) –c $(CPPFLAGS) $(CFLAGS)”。(建议使用“.cc”作为C++源文件的后缀,而不是“.C”)

3、编译Pascal程序的隐含规则。
“<n>;.o”的目标的依赖目标会自动推导为“<n>;.p”,并且其生成命令是“$(PC) –c$(PFLAGS)”。

4、编译Fortran/Ratfor程序的隐含规则。
“<n>;.o”的目标的依赖目标会自动推导为“<n>;.r”或“<n>;.F”或“<n>;.f”,并且其生成命令是:
“.f” “$(FC) –c $(FFLAGS)”
“.F” “$(FC) –c $(FFLAGS) $(CPPFLAGS)”
“.f” “$(FC) –c $(FFLAGS) $(RFLAGS)”

5、预处理Fortran/Ratfor程序的隐含规则。
“<n>;.f”的目标的依赖目标会自动推导为“<n>;.r”或“<n>;.F”。这个规则只是转换Ratfor或有预处理的Fortran程序到一个标准的Fortran程序。其使用的命令是:
“.F” “$(FC) –F $(CPPFLAGS) $(FFLAGS)”
“.r” “$(FC) –F $(FFLAGS) $(RFLAGS)”

6、编译Modula-2程序的隐含规则。
“<n>;.sym”的目标的依赖目标会自动推导为“<n>;.def” ,并且其生成命令是: “$(M2C) $(M2FLAGS) $(DEFFLAGS)”。“<n.o>;” 的目标的依赖目标会自动推导为“<n>;.mod”,并且其生成命令是:“$(M2C) $(M2FLAGS) $(MODFLAGS)”。

7、汇编和汇编预处理的隐含规则。
“<n>;.o” 的目标的依赖目标会自动推导为“<n>;.s”,默认使用编译品“as”,并且其生成命令是: “$(AS) $(ASFLAGS)” 。 “<n>;.s”的目标的依赖目标会自动推导为 “<n>;.S” ,默认使用C预编译器“cpp”,并且其生成命令是:“$(AS) $(ASFLAGS)”。

8、链接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)相关联,那么,你最好写出自己的生成规则,不然,隐含规则会报错的。

9、Yacc C程序时的隐含规则。
“<n>;.c” 的依赖文件被自动推导为 “n.y” (Yacc生成的文件) , 其生成命令是: “$(YACC) $(YFALGS)”。(“Yacc”是一个语法分析器,关于其细节请查看相关资料)

10、Lex C程序时的隐含规则。
“<n>;.c”的依赖文件被自动推导为“n.l” (Lex生成的文件),其生成命令是: “$(LEX) $(LFALGS)”。(关于“Lex”的细节请查看相关资料)

11、Lex Ratfor程序时的隐含规则。
“<n>;.r”的依赖文件被自动推导为“n.l” (Lex生成的文件),其生成命令是: “$(LEX)
$(LFALGS)”。

12、从C程序、Yacc文件或Lex文件创建Lint库的隐含规则。
“<n>;.ln” (lint生成的文件) 的依赖文件被自动推导为 “n.c” , 其生成命令是: “$(LINT) $(LINTFALGS) $(CPPFLAGS) -i”。对于“<n>;.y”和“<n>;.l”也是同样的规则。

隐含规则使用的变量

    在隐含规则中的命令中,基本上都是使用了一些预先设置的变量。你可以在你的makefile中改变这些变量的值,或是在make的命令行中传入这些值,或是在你的环境变量中设置这些
值,无论怎么样,只要设置了这些特定的变量,那么其就会对隐含规则起作用。当然,你也可以利用make的“-R”或“--no–builtin-variables”参数来取消你所定义的变量对隐含规则的作用。

1、关于命令的变量。
AR
函数库打包程序。默认命令是“ar”。
AS
汇编语言编译程序。默认命令是“as”。
CC
C语言编译程序。默认命令是“cc”。
CXX
C++语言编译程序。默认命令是“g++”。
CO
从 RCS文件中扩展文件程序。默认命令是“co”。
CPP
C程序的预处理器(输出是标准输出设备)。默认命令是“$(CC) –E”。
RM
删除文件命令。默认命令是“rm –f”。

2、关于命令参数的变量
如果没有指明其默认值,那么其默认值都是空。

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文件

转载于:https://my.oschina.net/u/1783725/blog/716858

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值