linux makefile

跟我一起学习makefile

编译链接

在此,我想多说关于程序编译的一些规范和方法,一般来说,无论是C、C++、还是pas,首先要把源文件编译成中间代码文件,在Windows下也就是 .obj 文件,UNIX下是 .o 文件,即 Object File,这个动作叫做编译(compile)。然后再把大量的Object File合成执行文件,这个动作叫作链接(link)。 编译时,编译器需要的是语法的正确,函数与变量的声明的正确。对于后者,通常是你需要告诉编译器头文件的所在位置(头文件中应该只是声明,而定义应该放在C/C++文件中),只要所有的语法正确,编译器就可以编译出中间目标文件。一般来说,每个源文件都应该对应于一个中间目标文件(O文件或是OBJ文件)。链接时,主要是链接函数和全局变量,所以,我们可以使用这些中间目标文件(O文件或是OBJ文件)来链接我们的应用程序。链接器并不管函数所在的源文件,只管函数的中间目标文件(Object File),在大多数时候,由于源文件太多,编译生成的中间目标文件太多,而在链接时需要明显地指出中间目标文件名,这对于编译很不方便,所以,我们要给中间目标文件打个包,在Windows下这种包叫“库文件”(Library File),也就是 .lib 文件,在UNIX下,是Archive File,也就是 .a 文件。
总结一下,源文件首先会生成中间目标文件,再由中间目标文件生成执行文件。在编译时,编译器只检测程序语法,和函数、变量是否被声明。如果函数未被声明,编译器会给出一个警告,但可以生成Object File。而在链接程序时,链接器会在所有的Object File中找寻函数的实现,如果找不到,那到就会报链接错误码(Linker Error),在VC下,这种错误一般是:Link 2001错误,意思说是说,链接器未能找到函数的实现。你需要指定函数的Object File.

makefile的规则

1)如果这个工程没有编译过,那么我们的所有C文件都要编译并被链接。
2)如果这个工程的某几个C文件被修改,那么我们只编译被修改的C文件,并链接目标程。
3)如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的C文件,并链接目标程序。
target … : prerequisites …
command

变量的定义和使用

target=main
objects=main.o fun.o

( t a r g e t ) : (target): (target):(objects)
gcc -o $(target) $(objects)

make自动推导

只要make看到一个[.o]文件,它就会自动的把[.c]文件加在依赖关系中。
edit : $(objects)
cc -o edit $(objects)
main.o : defs.h
kbd.o : defs.h command.h

头文件引用

$(objects) : defs.h
kbd.o command.o files.o : command.h
display.o insert.o search.o files.o : buffer.h

清空目标文件的规则

.PHONY : clean
clean :
-rm edit $(objects)
前面说过,.PHONY意思表示clean是一个“伪目标”,。而在rm命令前面加了一个小减号的意思就是,也许某些文件出现问题,但不要管,继续做后面的事。当然,clean的规则不要放在文件的开头,不然,这就会变成make的默认目标,相信谁也不愿意这样。不成文的规矩是——“clean从来都是放在文件的最后”。

引用其他的makefile

include foo.make *.mk $(bar)

make的工作方式

GNU的make工作时的执行步骤入下:(想来其它的make也是类似)
1、读入所有的Makefile。
2、读入被include的其它Makefile。
3、初始化文件中的变量。
4、推导隐晦规则,并分析所有规则。
5、为所有的目标文件创建依赖关系链。
6、根据依赖关系,决定哪些目标要重新生成。
7、执行生成命令。

函数

1、objects := $(wildcard *.o)
这种用法由关键字“wildcard”指出,关于Makefile的关键字,我们将在后面讨论。
2、patsubst
$(patsubst ,, )
名称:模式字符串替换函数——patsubst。
功能:查找 中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式,如果匹配的话,则以替换。这里,可以包括通配符“%”,表示任意长度的字串。如果中也包含“%”,那么,中的这个“%”将是中的那个“%”所代表的字串。(可以用“\”来转义,以“%”来表示真实含义的“%”字符)
返回:函数返回被替换过后的字符串。

自动化变量

@ 表 示 规 则 中 的 目 标 文 件 集 。 在 模 式 规 则 中 , 如 果 有 多 个 目 标 , 那 么 , " @ 表示规则中的目标文件集。在模式规则中,如果有多个目标,那么," @"@“就是匹配于
目标中模式定义的集合。
仅 当 目 标 是 函 数 库 文 件 中 , 表 示 规 则 中 的 目 标 成 员 名 。 例 如 , 如 果 一 个 目 标 是 " f o o . a ( b a r . o ) " , 那 么 , " % 仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是"foo.a (bar.o)",那么," "foo.a(bar.o)""%“就是"bar.o”,”$@“就是"foo.a”。如果目标不是函数库文件(Unix下是[.a],Windows下是[.lib]),那么,其值为空。
&lt; 依 赖 目 标 中 的 第 一 个 目 标 名 字 。 如 果 依 赖 目 标 是 以 模 式 ( 即 &quot; &lt; 依赖目标中的第一个目标名字。如果依赖目标是以模式(即&quot;%&quot;)定义的,那么&quot; <"<“将是符合模式的一系列的文件集。注意,其是一个一个取出来的。
$?
所有比目标新的依赖目标的集合。以空格分隔。
$^
所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的,那个这个变量会去除重复的依赖目标,只保留一份。
+ 这 个 变 量 很 像 &quot; + 这个变量很像&quot; +"^”,也是所有依赖目标的集合。只是它不去除重复的依赖目标。
∗ 这 个 变 量 表 示 目 标 模 式 中 &quot; * 这个变量表示目标模式中&quot;%&quot;及其之前的部分。如果目标是&quot;dir/a.foo.b&quot;,并且目标的模式是&quot;a.%.b&quot;,那么,&quot; "“的值就是"dir/a.foo”。这个变量对于构造有关联的文件名是比较有较。如果目标中没有模式的定义,那么" ∗ &quot; 也 就 不 能 被 推 导 出 , 但 是 , 如 果 目 标 文 件 的 后 缀 是 m a k e 所 识 别 的 , 那 么 &quot; *&quot;也就不能被推导出,但是,如果目标文件的后缀是make所识别的,那么&quot; "make"“就是除了后缀的那一部分。例如:如果目标是"foo.c”,因为".c"是make所能识别的后缀名,所以," ∗ &quot; 的 值 就 是 &quot; f o o &quot; 。 这 个 特 性 是 G N U m a k e 的 , 很 有 可 能 不 兼 容 于 其 它 版 本 的 m a k e , 所 以 , 你 应 该 尽 量 避 免 使 用 &quot; *&quot;的值就是&quot;foo&quot;。这个特性是GNU make的,很有可能不兼容于其它版本的make,所以,你应该尽量避免使用&quot; ""foo"GNUmakemake使"",除非是在隐含规则或是静态模式中。如果目标中的后缀是make所不能识别的,那么"$"就是空值。

( @ D ) 表 示 &quot; (@D) 表示&quot; (@D)"@“的目录部分(不以斜杠作为结尾),如果” @ &quot; 值 是 &quot; d i r / f o o . o &quot; , 那 么 &quot; @&quot;值是&quot;dir/foo.o&quot;,那么&quot; @""dir/foo.o""(@D)“就是"dir”,而如果"$@“中没有包含斜杠的话,其值就是”."(当前目录)。
( @ F ) 表 示 &quot; (@F) 表示&quot; (@F)"@“的文件部分,如果” @ &quot; 值 是 &quot; d i r / f o o . o &quot; , 那 么 &quot; @&quot;值是&quot;dir/foo.o&quot;,那么&quot; @""dir/foo.o""(@F)“就是"foo.o”," ( @ F ) &quot; 相 当 于 函 数 &quot; (@F)&quot;相当于函数&quot; (@F)""(notdir @ ) &quot; 。 &quot; @)&quot;。 &quot; @)""(*D)"
( ∗ F ) &quot; 和 上 面 所 述 的 同 理 , 也 是 取 文 件 的 目 录 部 分 和 文 件 部 分 。 对 于 上 面 的 那 个 例 子 , &quot; (*F)&quot; 和上面所述的同理,也是取文件的目录部分和文件部分。对于上面的那个例子,&quot; (F)""(*D)“返回"dir”,而” ( ∗ F ) &quot; 返 回 &quot; f o o &quot; &quot; (*F)&quot;返回&quot;foo&quot; &quot; (F)""foo""(%D)"
( 分 别 表 示 了 函 数 包 文 件 成 员 的 目 录 部 分 和 文 件 部 分 。 这 对 于 形 同 &quot; a r c h i v e ( m e m b e r ) &quot; 形 式 的 目 标 中 的 &quot; m e m b e r &quot; 中 包 含 了 不 同 的 目 录 很 有 用 。 &quot; (%F)&quot; 分别表示了函数包文件成员的目录部分和文件部分。这对于形同&quot;archive(member)&quot;形式的目标中的&quot;member&quot;中包含了不同的目录很有用。 &quot; ("archive(member)""member""(<D)”
( &lt; F ) &quot; 分 别 表 示 依 赖 文 件 的 目 录 部 分 和 文 件 部 分 。 &quot; (&lt;F)&quot; 分别表示依赖文件的目录部分和文件部分。 &quot; (<F)""(^D)”
( F ) &quot; 分 别 表 示 所 有 依 赖 文 件 的 目 录 部 分 和 文 件 部 分 。 ( 无 相 同 的 ) &quot; (^F)&quot; 分别表示所有依赖文件的目录部分和文件部分。(无相同的) &quot; (F)""(+D)”
( + F ) &quot; 分 别 表 示 所 有 依 赖 文 件 的 目 录 部 分 和 文 件 部 分 。 ( 可 以 有 相 同 的 ) &quot; (+F)&quot; 分别表示所有依赖文件的目录部分和文件部分。(可以有相同的) &quot; (+F)""(?D)”
“$(?F)”
分别表示被更新的依赖文件的目录部分和文件部分。

模式的匹配

一般来说,一个目标的模式有一个有前缀或是后缀的"%",或是没有前后缀,直接就是一个"%"。因为"%“代表一个或多个字符,所以在定义好了的模式中,我们把”%“所匹配的内容叫做"茎”,例如"%.c"所匹配的文件"test.c"中"test"就是"茎"。因为在目标和依赖目标中同时有"%“时,依赖目标的"茎"会传给目标,当做目标中的"茎”。
当一个模式匹配包含有斜杠(实际也不经常包含)的文件时,那么在进行模式匹配时,目录部分会首先被移开,然后进行匹配,成功后,再把目录加回去。在进行"茎"的传递时,我们需要知道这个步骤。例如有一个模式"e%t",文件"src/eat"匹配于该模式,于是"src/a"就是其"茎",如果这个模式定义在依赖目标中,而被依赖于这个模式的目标中又有个模式"c%r",那么,目标就是"src/car"。("茎"被传递)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值