makefile简介
对于linux来说,IDE环境并不多,就算有IDE也没有命令行好用。在这种情况下,没有ID E的帮助,要管理一个工程的编译是非常困难的,此时make工具诞生了。
make将管理一个工程的规则,写入文件中,然后,make读取这里面的规则,来决定如何编译一个工程(比如,那些文件先编译,那些文件后编译等)
那么这里面的重中之重就是规则了。下面来一个简单的规则文件。
main:main.c print.c
gcc –o main main.c print.c
简单说明如下:
第一行:表示一个规则的前部,前部由两部分组成:冒号之前的称为目标,冒号之后的称为依赖。就是说:要生成main这个目标,必须依赖main.c和print.c这两个文件。
第二行:表示一个规则的后部,后部表示的是生成这个目标要如何操作。比如上例:要生成main这个目标,就是执行gcc –o main main.c print.c来生成。
同时make也会根据依赖的修改时间来决定是否要更新目标,当依赖比目标的时间新时,表示目标需要更新,因此执行规则的后部。
我们将上面的规则写入一个名字为makefile的文件中。这样当我们直接执行make的时候,make会去读取当前文件夹下的makefile,并解析这个规则。
**
makefile的变量
**
makefile中规则是真正的主体,但是如果有了变量,编写起来会更简单。makefile中的变量分为两种:直接展开变量,延时展开变量
直接展开变量是通过“:=”进行赋值的。
延时展开变量是通过“=”进行赋值的。例子如下:
name := wanbiao
name = wanbiao
第一个为直接展开变量,后一个为延时展开变量。
关于这两种变量的区别,后面有详细描述。
这里先讨论变量的命名规范。
一 长度:makefile的变量并没有规定变量的长度,但是笔者这里推荐最好不好超过31个字符,只是经验之谈,并没有什么具体的例子支撑。
二 名字:变量名不能包括:# =等特殊字符。gnu make并没有明确说变量不能包含点号。但是这里并不推荐使用此种变量命名法,因为在后续的make版本中,很肯能将使用这些特殊字符。更加通用的做法是,使用字母,数字,下划线,进行变量的命名。
有了变量的名字,来看看变量的值。
makefile中变量的值,可以是任何有效字符,如果遇到特殊字符,可以使用反斜杠进行转义,变量的值,可以包含在单引号,双引号之内,也可以不用包含。例子如下
name := wanbiao
name := “wanbiao”
name :=’wanbiao’
name := wanbiao zhangsan lisi
makefile将变量的值都当作字符串进行处理。尽管你赋值给他的是一个数字,浮点数,例子如下:
age := 20
weight := 59.5
makefile的赋值操作
makefile的赋值分两种,一种是直接展开,另外一种是延时展开。
直接展开是:在变量赋值时,当一个变量引用另外一个变量,直接将另外的变量展开,赋值给当前的变量。
延时展开是:在变量引用时,才将一个变量引用的两外一个变量展开。
例子如下:
name = $(firstname) $(lastname)
firstname = wan
lastname = biao
display:
@echo $(name)
那么当需要更新display时,会打印wan biao.解释如下:
当make扫描到第一行时,并不会马上将$(firstname)和$(lastname)的值展开。而是要在引用的时候,才展开,即当执行echo $(name) 的时候,才去展开,发现$(firstname)为wan $(lastname)为biao,因此输出结果为wan biao
这就是延时展开。下面看看一个立即展开的例子
我们将上面的例子中的“=”换成“:=”如下:
name := $(firstname) $(lastname)
firstname := wan
lastname := biao
display:
@echo $(name)
当需要更新display的时候,会打印为空。解释如下:
当扫描到第一行的时候,会立即将$(firstname) 和$(lastname)的值展开,因为这两个变量都没有在前面赋值过,所以name的值为空。当执行到echo $(name)的时候,直接打印出name的值,为空。
对于这两种赋值方法的异同如下:
延时展开:
优点:可以先使用,后定义。
缺点:容易陷入死循环中。当一个变量直接或者间接的引用自己的时候,容易出现死循环,如下:
name = wanbiao
name += $(name) zhangsan
当引用时展开它,发现引用了$(name),然后继续展开,又发现了引用$(name),一直这样下去,陷入无限循环之中
立即展开:
优点:不会陷入死循环中
缺点:无法在定义之前使用变量。
建议:在大型项目中使用立即展开“:=”,这样更符号先定义后使用,同时排错起来也相对较易。
变量赋值的其他操作
一个变量可能需要在后面增加值,此时可以使用+=如下:
name := wanbiao
name += zhangsan
此时,name的值为wanbiao zhangsan。同样当+=后面有其他变量的引用时,name是立即展开还是延时展开,取决于+=前面变量的定义,如下:
name := wanbiao
name += zhangsan $(others)
因为name是使用“:=”为立即展开,所以$(others)也是立即展开。
如果要根据一个变量以前是否赋值来决定是否要赋于新的值,可以使用 ?= 用法如下:
name ?= zhangsan
表示:当name没有赋值的时候,将zhangsan赋值给name,当name已经有值了,那么就不进行操作。
此种操作还可以使用后面讲述的条件判断。
变量的引用
在makefile中变量的引用是使用关键字$(变量名)或者${变量名},如果变量名只有一个字符,那么可以省略小括号或者大括号。如下:
n := wanbiao
lastname := $(n)
display:
echo ${n}
上面的echo ${n} 还可以写成echo $(n) 或者echo $n.
注意:在shell中变量的引用同样使用了$加变量名的情况,并且变量名不用()或者{}括起来。
定义一个空格变量
当需要传递一个空格的时候,不能直接书写空格,原因有二:1,空格并不容易阅读,常常造成误会。2.空格常用作分隔符,使用不当,很可能被当作分隔符,而提示错误。可以使用一个变量来代替空格,以弥补这种缺憾。
同时需要注意的是,在变量的赋值时,变量值的前导空格会被忽略掉,如下,将会忽略掉前面的空格。
name := wan biao
wan前面的空格将会忽略,所以直接将空格赋值给变量并不能实现。我们可以使用如下的方法,进行规避。
empty :=
#定义一个空格。
space :=$(empty) $(empty)
通过在两个$(empty)中间输入一个空格,来实现空格的定义。
变量的替换引用
makefile可以在变量使用的时候进行一种替换。如下:
name := wanbiao
realename := $(name:biao=jie)
表示的是:将name变量值末尾的biao替换成jie,即wanjie,然后再将结果赋值给realename变量。
此种用法常用来替换后缀,比如将.c文件替换成.o文件,如下:
src := a.c b.c c.c
object := $(src:.c=.o)
all:$(object)
gcc –o main $(object)
override关键字
变量不仅可以通过在makefile中定义,还可以在make的命令行中进行赋值。如下的makefile文件
name := wanbiao
display :
echo $(name)
在执行的时候,运行如下的命令
make name=zhangsan
那么运行的结果将是zhangsan。这是因为在make命令行中赋值的变量会覆盖掉makefile文件中的同名变量。
有时为了防止这种情况的出现,可以在makefile中使用override关键字。如下:
override name := wanbiao
display:
echo $(name)
那么这种关键字的用处在什么情况下呢,通常有如下的使用情况,
override name += wanbiao
display:
echo $(name)
那么无论在make的命令行下指定了什么样的name值,那么name一定会有一个wanbiao的值。这种方法可以在一些命令行的参数必须保留某个参数时使用,如下:
override CFLAGS += -g
这样无论在命令行下制定了CFLAGS是什么,那么当编译c文件时,一定会添加一个-g选项,这个选项用来在gdb调试时使用
目标指定变量
当在makefile中定义一个变量,那么这个变量在整个文件内都可使用。我们也可以定义一个在指定的目标中才能使用的变量,这种变量成为目标指定变量,格式如下:
目标:变量声明
举例如下:
%.o :CFLAGS +=-o
表示在满足%.o这个目标的所有上下文中,CFLAGS的变量都会增加-o值