Make
1. 学习make的必要性
在Linux中,有一个用来维护程序模块关系和生成可执行程序的工具-make。他可以根据程序模块的修改情况重新编译链接生成中间代码或最终的可执行程序。执行make 命令,需要一个名为“makefile”或“Makefile”的文本文件,这个文件定义了整个项目的编译规则。它定义了模块间的依赖关系,指定文件的编译顺序,以及编译所使用的命令。有了make命令和Makefile文件,整个项目的源程序文件可以自动编译,极大地提高了软件的开发效率。
2. make的一般使用
目标文件列表 分隔符 依赖文件列表[; 命令]
[命令] 注意:如果命令换行写,前面加Tab键。
[命令]
main.o : main.c defs.h
cc -c main.c
main.o : main.c defs.h;cc -c main.c
过时:是指一个文件生成后,用来生成该文件的源文件或头文件被修改了,导致生成该文件所需要的源文件或头文件的修改时间比生成该文件的时间完。
编译顺序:
- 如果只输入
make
命令。那么, make会在当前目录下找名字
“Makefile”或“makefile”
的文件。也可以命名为其他的名字make -f othername
- 它会找文件中的第一个目标文件(target)并把这个文件作为最终的目标文件。
- 观察目标文件是否过时,如果过时,执行规则后面的命令。否则,不执行。
- 从头到尾扫描完一遍Makefile文件后,make回溯一遍。
Makefile告诉make命令如何编译和链接这几个文件
- 如果这个工程没有编译过,那么我们的所有C文件都要编译并被链接。
- 如果这个工程的某几个C文件被修改,那么我们只编译被修改的C文件,并链接目标程序.
- 如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的C文件,并链接目标程序。
3.Makefile文件的构成
Makefile里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释。
- 显式规则。显式规则说明了,如何生成一个或多的的目标文件。这是由Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。
- 隐晦规则。由于我们的make有自动推导的功能,所以隐晦的规则可以让我们比较粗糙地简略地书写Makefile,这是由make所支持的。
- 变量的定义。在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点你C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。
- 文件指示。其包括了三个部分,一个是在一个Makefile中引用另一个Makefile,就像C语言中的include一样;另一个是指根据某些情况指定Makefile中的有效部分,就像C语言中的预编译#if一样;还有就是定义一个多行的命令。有关这一部分的内容,我会在后续的部分中讲述。
- 注释。Makefile中只有行注释,和UNIX的Shell脚本一样,其注释是用“#”字符,这个就像C/C++中的“//”一样。如果你要在你的Makefile中使用“#”字符,可以用反斜框进行转义,如:“#”。
显示规则
main.o : main.c defs.h
cc -c main.c
clean:
rm *.o
- 在规则中使用通配符
*
通配符代替了你一系列的文件,如*.c
表示所有后缀为c的文件
。 - 文件搜寻
- 如果定义了
VPATH
变量,make就会在当前目录找不到的情况下,到所指定的目录中去找寻文件了。VPATH = src:../headers
- 另一个设置文件搜索路径的方法是使用make的“vpath”关键字,它可以指定不同的文件在不同的搜索目录中。
vpath < pattern> < directories>
为符合模式< pattern>的文件指定搜索目录。
vpath < pattern>
清除符合模式< pattern>的文件的搜索目录。
vpath
清除所有已被设置好了的文件搜索目录。
注意:vapth
使用方法中的< pattern>
需要包含%
字符。%
的意思是匹配零或若干字符,例如,%.h
表示所有以.h
结尾的文件。
- 如果定义了
- 伪目标
“伪目标”并不是一个文件,只是一个标签,所以make无法生成它的依赖关系和决定它是否要执行。当然,“伪目标”的取名不能和文件名重名,不然其就失去了“伪目标”的意义了。为了避免和文件重名的这种情况,我们可以使用一个特殊的标记.PHONY
来显示地指明一个目标是伪目标
,向make
说明,不管是否有这个文件,这个目标就是伪目标
。
于是整个过程可以这样写:
.PHONY: clean
clean:
rm *.o
在Makefile中,一个伪目标可以有自己的依赖。在一个目录下如果需要生成多个可执行程序,可以在一个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
-
多目标
一个规则中可以有多个目标,规则所定义的命令对所有的目标都有效.mod1.o mod2.o mod3.o : commmand.c
这个规则同时实现给3个目标文件制定一个依赖文件.由于隐含规则的作用,它等价与下面的规则:
mod1.o : mod1.c command.c
gcc -c mod1.c -o mod1.o
mod2.o : mod2.c command.c
gcc -c mod2.c -o mod2.o
mod2.o : mod2.c command.c
gcc -c mod2.c -o mod2.o
隐含规则
make自动推导
main.o : main.c defs.h
cc -c main.c
main.o : defs.h
使用变量
-
引用变量
变量的引用方式是:$(变量)
或者${变量}
,如果要使用字符$
,要用$$
来表示. -
定义变量
1.递归展开变量=
foo = $(bar) bar = $(ugh) ugh = Huh? all: echo $(foo) 执行“make all”将会打出变量$(foo)的值是“Huh?”
2.立即展开变量
:=
x := foo y := $(x) bar x := later 其等价于: y := foo bar x := later
-
预定义变量
常用的自动变量:
$@:表示一个规则中的目标文件
$%:当规则的目标文件是一个静态库文件时,代表静态库的一个成员库.
$<:规则中的第一个依赖文件名
$>:规则中的第一个依赖文件名,它的值是库名.
$?:所有比目标文件新的依赖文件列表,以空格分隔.
$^:规则的所有依赖文件,使用空格分隔.
$+:保留了依赖文件中重复出现的文件.主要用在程序链接时库的交叉引用场合.
$*:他的值是目标场合去掉后缀后的名称.
使用库
在大型软件开发中,通常把编译好的模块按照功能不同放在不同的库中.在Linux中,最后链接生成可执行文件时,如果链接的是一般.o文件,是把整个.o文件的内容插入到可执行文件中.而如果链接的是库,则只从库中找出程序需要的变量和函数,把它们装入到可执行文件中.使用库可以大大节省空间,所以系统提供的标准函数一般都是以库的形式提供.
库中的文件一般都是库的成员,成员的表示形式为:库员(成员名)
mylib.a(file.o)
通常使用ar
命令对它进行维护和管理
ar -ruv 库名 目标文件名
使用条件语句
ifeq
表示条件语句的开始,并指定了一个比较条件(相等),还可以使用关键字ifneq
,如果两个参数不相等.
else
当条件不满足时要执行的部分
endif
表示一个条件语句的结束,任何一个条件表达式都必须要以它结束
libs_for_gcc=-lgnu
normal_liba=
foo:foo.c
ifeq($(CC),gcc)
$(CC) -o foo foo.c $(libs_for_gcc)
else
$(CC) -o foo foo.c $(normal_liba)
endif
ifdef
用来判断一个变量是否已经定义,ifndef
用来判断一个变量是否没有被定义.
bar=
foo=$(bar)
ifdef foo
frobozz=yes
else
frobozz=no
endif
make参数
-C dir 或者 --directory=DIR
在读取Makefile文件之前,先切换到dir
目录下.-d
make执行时打印出所有的调试信息.-e
不允许在Makefile中对系统环境变量进行重新赋值.-f filename
使用指定文件作为Makefile文件-i
忽略执行是产生的错误,不退出make