欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

linux makefile

程序员文章站 2022-05-07 12:08:00
...

跟我一起学习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

(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”或“回车”“换行”分隔)是否符合模式,如果匹配的话,则以替换。这里,可以包括通配符“%”,表示任意长度的字串。如果中也包含“%”,那么,中的这个“%”将是中的那个“%”所代表的字串。(可以用“\”来转义,以“%”来表示真实含义的“%”字符)
返回:函数返回被替换过后的字符串。

自动化变量

@"@ 表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,"@“就是匹配于
目标中模式定义的集合。
"foo.a(bar.o)""% 仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是"foo.a (bar.o)",那么,"%“就是"bar.o”,”[email protected]“就是"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;make&quot;*&quot;也就不能被推导出,但是,如果目标文件的后缀是make所识别的,那么&quot;“就是除了后缀的那一部分。例如:如果目标是"foo.c”,因为".c"是make所能识别的后缀名,所以,"&quot;&quot;foo&quot;GNUmakemake使&quot;*&quot;的值就是&quot;foo&quot;。这个特性是GNU make的,很有可能不兼容于其它版本的make,所以,你应该尽量避免使用&quot;",除非是在隐含规则或是静态模式中。如果目标中的后缀是make所不能识别的,那么"$"就是空值。

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

模式的匹配

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

相关标签: 编译