Makefile学习心得

本文详细介绍了Makefile的核心概念,包括其在自动化编译中的重要作用,程序编译的规范和方法,以及Makefile的规则、变量使用和自动推导功能。通过实例展示了如何编写和优化Makefile,帮助提高软件开发效率。

阅读 跟我一起写Makefile-陈皓pdf  学习笔记

                   --Tengteng

一、makefile 概述:

makefile 关系到了整个工程的编译规则。一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile 定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为 makefile 就像一个 Shell 脚本一样,其中也可以执行操作系统的命令。 makefile 带来的好 处就是——“自动化编译”,一旦写好,只需要一个 make 命令,整个工程完全自动编译,极大的提高了软件开发的效率。

 

二、关于程序编译的一些规范和方法:

一般来说,无论是 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 文件。

 

三、Makefile 的规则:

1、

target ... : prerequisites ... 

command

... 

...

 

target 也就是一个目标文件,可以是 Object File,也可以是执行文件。还可以是一个 标签(Label),对于标签这种特性,在后续的“伪目标”章节中会有叙述。 prerequisites 就是,要生成那个 target 所需要的文件或是目标。 command 也就是 make 需要执行的命令。(任意的 Shell 命令)

prerequisites 中如果有一个以上的文件比 target 文件要新的话,command 所定义的命令就会被执行。这就是 Makefile 的规则。也就是 Makefile 中最核心的内容。

例如:

operator:add.o sub.o \                 #反斜杠(\)是换行符的意思

operator.o                 

gcc add.o sub.o operator.o -o operator           1

add.o:add.c

gcc -c add.c -o add.o                            2

sub.o:sub.c 

gcc -c sub.c -o sub.o                            3

operator.o:operator.c                   

gcc -c operator.c -o operator.o                  4

clean:

rm -f *.o operator                 #定义了如何生成目标文件的操           作系统命令,一定要 以一个 Tab 键作为开头。

‘#’号为注释

如果你要在你的 Makefile 中使用“#”字符,可以用反斜框进行 转义,如:“\#”。

 

 

make 一下生成 operator的执行文件,

make clean 执行rm -f *.o operator

clean: 不是文件 只是动作的名字

 

 

执行顺序: 2 -> 3 -> 4 -> 1

 

2、makefile 中使用变量:

在 makefile 中我们可以使用变量。makefile 的变量也就是一个字符串,理解成 C 语言中的宏可能会更好。

比如,我们声明一个变量,叫 objects, OBJECTS, objs, OBJS, obj, 或是 OBJ,反正 不管什么啦,只要能够表示 obj 文件就行了。我们在 makefile 一开始就这样定义: 

objects = main.o kbd.o command.o display.o 

\ insert.o search.o files.o utils.o 

于是,我们就可以很方便地在我们的 makefile 中以“$(objects)”的方式来使用这个变量了,于是我们的改良版 makefile 就变成下面这个样子:

 

objects = main.o kbd.o command.o display.o 

\ insert.o search.o files.o utils.o 

 

edit : $(objects) 

cc -o edit $(objects) 

main.o : main.c defs.h 

cc -c main.c 

kbd.o : kbd.c defs.h command.h 

cc -c kbd.c 

command.o : command.c defs.h command.h 

cc -c command.c 

display.o : display.c defs.h buffer.h 

cc -c display.c 

...

...

...

 

clean :

rm -f $(objects) edit

 

 

3、让 make 自动推导:

GNU (GNU是一个自由软件工程项目) 的 make 很强大,它可以自动推导文件以及文件依赖关系后面的命令,于是我们就没必 要去在每一个[.o]文件后都写上类似的命令,因为,我们的 make 会自动识别,并自己推导命令。 

只要 make 看到一个[.o]文件,它就会自动的把[.c]文件加在依赖关系中,如果 make 找到一个 whatever.o,那么 whatever.c,就会是 whatever.o 的依赖文件。并且 cc -c whatever.c 也会被推导出来,于是,我们的 makefile 再也不用写得这么复杂。我们的是新 的 makefile 又出炉了。

 

objects = main.o kbd.o command.o display.o 

\ insert.o search.o files.o utils.o 

 

edit : $(objects) 

cc -o edit $(objects)    //可省略

 

main.o : defs.h 

kbd.o : defs.h command.h 

command.o : defs.h command.h 

display.o : defs.h buffer.h 

...

...

...

 

.PHONY : clean

clean :

rm -f $(objects) edit

 

这种方法,也就是 make 的“隐晦规则”。上面文件内容中,“.PHONY”表示,clean 是个伪目标文件。

 

 

 

4、另类风格的 makefile :

 

objects = main.o kbd.o command.o display.o

\ insert.o search.o files.o utils.o 

edit : $(objects) 

cc -o edit $(objects)

 

$(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)

 

这种风格,让我们的 makefile 变得很简单,但我们的文件依赖关系就显得有点凌乱了。 鱼和熊掌不可兼得。

 

5、清空目标文件的规则:

 

clean: 

rm edit $(objects) 

 

更为稳健的做法是: 

.PHONY : clean 

clean : 

-rm edit $(objects) 

 

前面说过,.PHONY 意思表示 clean 是一个“伪目标”,。而在 rm 命令前面加了一个小减号的意思就是,也许某些文件出现问题,但不要管,继续做后面的事。

当然,clean 的规则不要放在文件的开头,不然,这就会变成 make 的默认目标,相信谁也不愿意这样。不成文的规矩是——“clean 从来都是放在文件的最后”。

 

 

 

 

 

  以上为基础。

 

四、Makefile 总述:

1、Makefile 的文件名:

 

默认的情况下,make 命令会在当前目录下按顺序找寻文件名为“GNUmakefile”、 “makefile”、“Makefile”的文件,找到了解释这个文件。在这三个文件名中,最好使用 “Makefile”这个文件名,因为,这个文件名第一个字符为大写,这样有一种显目的感觉。 最好不要用“GNUmakefile”,这个文件是 GNU 的 make 识别的。有另外一些 make 只对全小 写的“makefile”文件名敏感,但是基本上来说,大多数的 make 都支持“makefile”和 “Makefile”这两种默认文件名。

 当 然 , 你可以使用别的 文 件 名 来 书 写 Makefile , 比 如 : “Make.Linux” , “Make.Solaris”,“Make.AIX”等,如果要指定特定的 Makefile,你可以使用 make 的 “-f”和“--file”参数,如:make -f Make.Linux 或 make --file Make.AIX。

 

2、make 支持 三各通配符:“*”,“?”和“[...]”

 

3、文件搜寻 :

在一些大的工程中,有大量的源文件,我们通常的做法是把这许多的源文件分类,并存放在不同的目录中。

所以,当 make 需要去找寻文件的依赖关系时,你可以在文件前加上路 径,但最好的方法是把一个路径告诉 make,让 make 在自动去找。

 Makefile 文件中的特殊变量“VPATH”就是完成这个功能的,如果没有指明这个变量, make 只会在当前的目录中去找寻依赖文件和目标文件。如果定义了这个变量,那么,make 就会在当当前目录找不到的情况下,到所指定的目录中去找寻文件了。 

 

VPATH = src:../headers 

 

上面的的定义指定两个目录,“src”和“../headers”,make 会按照这个顺序进行搜 索。目录由“冒号”分隔。(当然,当前目录永远是最高优先搜索的地方)

 

另一个设置文件搜索路径的方法是使用 make 的“vpath”关键字(注意,它是全小写 的),这不是变量,这是一个 make 的关键字,这和上面提到的那个 VPATH 变量很类似,但是 它更为灵活。它可以指定不同的文件在不同的搜索目录中。这是一个很灵活的功能。

它的使 用方法有三种: 

1、vpath <pattern> <directories>

为符合模式<pattern>的文件指定搜索目录<directories>。 

2、vpath <pattern>

清除符合模式<pattern>的文件的搜索目录。

3、vpath 

清除所有已被设置好了的文件搜索目录。 

 

vapth 使用方法中的需要包含“%”字符。“%”的意思是匹配零或若干字符, 例如,“%.h”表示所有以“.h”结尾的文件。<pattern>指定了要搜索的文件集,而<directories> 则指定了<pattern>的文件集的搜索的目录。

 

例如: vpath %.h ../headers

 

该语句表示,要求 make 在“../headers”目录下搜索所有以“.h”结尾的文件。(如果 某文件在当前目录没有找到的话) 

 

我们可以连续地使用 vpath 语句,以指定不同搜索策略。如果连续的 vpath 语句中出现 了相同的,或是被重复了的,那么,make 会按照 vpath 语句的先后顺 序来执行搜索。

如:  vpath %.c foo 

vpath % blish 

vpath %.c bar 

 

其表示“.c”结尾的文件,先在“foo”目录,然后是“blish”,最后是“bar”目录。

 

vpath %.c foo:bar 

vpath % blish 

 

而上面的语句则表示“.c”结尾的文件,先在“foo”目录,然后是“bar”目录,最后 才是“blish”目录。

 

4、伪目标 

 

伪目标一般没有依赖的文件。但是,我们也可以为伪目标指定所依赖的文件。伪目标同 样可以作为“默认目标”,只要将其放在第一个。

一个示例就是,如果你的 Makefile 需要 一口气生成若干个可执行文件,但你只想简单地敲一个 make 完事,并且,所有的目标文件都写在一个 Makefile 中,那么你可以使用“伪目标”这个特性: 

 

all : prog1 prog2 prog3 

.PHONY : all 

 

prog1 : prog1.o utils.o 

cc -o prog1 prog1.o utils.o

 

prog2 : prog2.o 

cc -o prog2 prog2.o

 

prog3 : prog3.o sort.o utils.o 

cc -o prog3 prog3.o sort.o utils.o 

 

我们知道,Makefile 中的第一个目标会被作为其默认目标。我们声明了一个“all”的 伪目标,其依赖于其它三个目标。由于伪目标的特性是,总是被执行的,所以其依赖的那三 个目标就总是不如“all”这个目标新。所以,其它三个目标的规则总是会被决议。也就达 到了我们一口气生成多个目标的目的。

“.PHONY : all”声明了“all”这个目标为“伪目 标”。 随便提一句,从上面的例子我们可以看出,目标也可以成为依赖。所以,伪目标同样也 可成为依赖。

看下面的例子: 

 

.PHONY: cleanall cleanobj cleandiff 

 

cleanall : cleanobj cleandiff 

rm program 

 

cleanobj : 

rm *.o

 

cleandiff : 

rm *.diff 

 

“make clean”将清除所有要被清除的文件。“cleanobj”和“cleandiff”这两个伪 目标有点像“子程序”的意思。我们可以输入“make cleanall”和“make cleanobj”和 “make cleandiff”命令来达到清除不同种类文件的目的。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值