在Makefile中,最重要的三个概念是:目标(target)、依赖关系(dependency)和命令(command)。目标是指要干什么,即运行make后生成什么;依赖是指明目标所依赖的其他目标;命令则告诉make如何生成目标,这三个概念是通过Makefile中的规则(rule)关联在一起的。
例 1 编辑一个名为 Makefile 的文件,文件内容如下:
all:
echo “Hello Lion, I love you”
然后在命令行中执行它,键入集合 make ,就能执行。编辑 makefile 文件时要注意,命令所在的行必须以 Tab 键开头。
在Makefile中,目标和命令组合在一起就形成了一个简单的规则,通过这个规则,我们告诉 make 要做什么。运行 make 命令时可以指定具体的目标加以选择。
例 2 继续编辑修改刚才的 Makefile 文件,如下:
all:
echo “Hello Lion, I love you”
test:
echo “Just for test, she is so beautiful”
综上,我们得到以下信息:一个Makefile中可以定义多个目标;调用 make 命令时,得告诉它我们希望构建的目标是什么,即要它执行哪个命令,第一个目标是默认执行的目标;当 make 得到目标后,先找到构建目标的对应规则,然后运行规则中的命令来达到构建目标的目的,一个规则中可以根据需要存在多条命令。
如果不想让 make 打印出每条要执行的命令,可以在命令前加上 @ 符号,如
all:
@ echo “Hello Lion, I love you”
test:
@echo “Just for test, she is so beautiful”
先决条件:在执行一个目标前,必须要先执行其他目标,即当前目标的执行是以其他目标的执行为条件。这个先决目标就是当前要执行的目标要依赖的目标。如把刚才的 Makefile 修改如下:
all: test
@echo “Hello Lion, I love you”
test:
@echo “Just for test, she is so beautiful”
然后再次执行命令 make ,运动结果如下:
$ make
Just for test, she is so beautiful!
Hello Lion, I love you!
从结果可以看到,test 目标先被构建了,然后才构建 all 目标,因为 test 目标是 all 目标的先决条件。出现这种目标依赖关系时, make 会从左到右(在同一规则中)和从上到下(在不同的规则中)的先后顺序先构建一个规则所依赖的每一个目标,形成一种“链式反应”。
在我看来,学会写简单的Makefile,阅读较复杂的makefile,是每一个Linux程序员都必须拥有的基本素质。Makefile可以自动识别哪些源文件被更改过,需要重新编译,那些不需要。从而节省大型工程重新编译的时间。规则如下:
如果这个工程没有编译过,那么我们的所有C文件都要编译并被链接。
如果这个工程的某几个C文件被修改,那么我们只编译被修改的C文件,并链接目标程。
如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的C文件,并链接目标程序。
学会编写Makefile,不仅仅有益于你在Linux下编写大型工程。同时也能帮助你理解编译原理。远离IDE,了解编译过程。
Makefile: 程序模块的内部关系决定了源程序编译和链接的顺序,通过建立makefile可以描述模块间的相互依赖关系。Make命令从中读取这些信息,然后根据这些 信息对程序进行管理和维护。在makefile里主要提供的是有关目标文件(即target)与依靠文件(即dependencyies)之间的关系,还 指明了用什么命令生成和更新目标文件。有了这些信息,make会处理磁盘上的文件,如果目的文件的时间标志(该文件生成或被改动进的时间)比任意一个依靠 文件旧,make就执行相应的命令,以便更新目的文件(目的文件不一定是最后的可执行文件,它可以是任何一个文件)。
1)makefile的基本单位是“规则”,即描述一个目标所依赖的文件或模块,并给出其生成和算法语言需要用到的命令。规则的格式如下:
目标[属性]
分隔符号 [依赖文件][命令列]
{《tab》命令列}
与Linux下面的命令格式相同,[]中的内容表示为可选择项,{}中的内容表示可出现多次。
A. 目标:目标文件列表,即要维护的文件列表。
B. 属性:表示该文件的属性。
C. 分隔符:用来分割目标文件和依赖文件的符号,如冒号“:”等。
D. 依赖文件:目标文件所依赖的文件的列表。
E. 命令列:重新生成目标文件的命令,可以有多条命令。
注 意:在makefile中,除了第一条命令,每一个命令行的开头必须是一个《tab》符号,也就是制表符,而不能因为制表符相当于4个空格而 不去键入tab符号。因为make命令是通过每一行的tab符号来识别命令行的。另外,对于第一条命令而言,不必用《tab》键,就可以直接 跟在依赖文件的列表后面。对于注释的了,起头应该用#符号,并用换行符号结束。如果要引用#符号,要用到“”。
2) make命令的使用格式为:
make [选项][宏定义][目标文件]
make命令有多个选项参数,列举参数含义如下:
A. -f:指定需要维护的目标。
B. -i:忽略运行makefile中命令产生的错误,不退出make.
C. -r:忽略内部规则。
D. -s:执行但是不显示所执行的命令。
E. -x:将所有的宏定义都输出到shell环境。
F. -V:列出make的版本号。
选项给出了make命令工作的方式方法,宏定义给出了makefile时所用的宏值,目标文件就是需要更新的文件列表。
3)使用伪目标:make命令的目标可分为实目标和伪目标两种。而有时需要用make命令来做些辅助性的工作,或者对多个文件进行维护。可以通过设置伪目标来实现。
4)指定需要维护的目标:一般make维护的是makefile中的第一个目标文件。但有时用户并不关心最终的目标文件如何。反而关心中间的目标文件。使用目标参数make的执行。
5)makefile 变量:makefile里主要包含了一些规则,除此之外就是变量定义,被称为宏定义。Makefile里的变量就像一个环境变量。事实上,环境变量在 make过程中可以看成make的变量。这些宏定义是大小写敏感的,一般使用大写字母。它们几乎可以从任何地方被引用,也可以被用来做很多事情,比如:
A.储存一个文件名列表:生成可执行文件的规则包含一些目标文件名作为依赖文件。在这个规则的命令行里,同样的那些文件被输送给gcc做为命令参数。如果在这里使用一个宏来储存所有目标文件名,那么就会很容易加入新的目标文件,而且不易出错。
B.储存可执行文件名:如果程序被用在一个非GCC的系统里,或者想使用一个不同的编译器,就必须将所有使用编译器的地方改成新的编译器名。但是如果使用一个宏来代替编译器名,那么只需要改变一个地方,其他所有地方的命令名就都改变了。
C.储 存编译器命令选项:假设想给所有的编译命令传递一组相同的选项(例如-Wall -O -g),如果把这组选项存入一个宏,那么可以把这个宏放在所有调用编译器的地方。而当要改变选项的时候,只需在宏定义的地方改变这个变量的内容。要定义一 个宏,只要在一行的开始写下这个宏的名字,后面跟一个“=“和要设定这个变量的值。以后引用这个变量时,写一个$符,后面是括号里的变量名。格式如下:
$(宏名) 或${宏名}
make将$符号作为引用的开始。如果要表示$符号,那么应用$$即可。宏引用还支持多层引用,在处理时按照顺序依次展开。当宏名是单个字符时,可以省略括号,宏定义可以在makefile文件中。
I.$@:扩展成当前规则的目标文件名
II.$《:扩展成依赖列表中的第一个依赖文件
III.$^:扩展成整个依赖的文件列表(除掉了里面所有重复的文件名)
IV.$?:表示目标文件中新的依赖文件的列表
V.$*:是表示依赖文件的文件名,不含扩展名
6)在makefile中使用函数:makefile里的函数跟它的变量很相似。在调用时,用一个$开始,是开括号,函数名,再空格,然后跟一列由逗号分隔的参数,最后用关括号结束。
以上,是对Makefile的一个简单入门介绍,一般,用以阅读大多数的Makefile都已经足够了。
评论
查看更多