makefile编写规则

Makefile书写格式

在大型程序开发时,通常会用到Makefile进行构建(build)。Makefile文件一般规定了源文件怎么去编译(compile),其内部的书写规则如下:

<target> : <prerequisites>
[tab]	<commands>

上面第一行冒号前面的部分,叫做目标(target),冒号后面的部分叫做前置条件(prerequisites);第二行必须由一个tab键起首,后面跟着命令(commands)。

目标是必需的,不可省略;前置条件和命令都是可选的,但是两者之中必须至少存在一个

目标target

一个目标(target)就可构成一条命令。目标通常是文件名,来指明Make命令所要构建的对象。它既可以是一个文件名,也可以是多个,之间用空格分隔即可。

除了文件名,目标还可以是某个操作的名字,这称为"伪目标"(phony target)。如:

clean:
	rm *.o

上面的目标clean,不是一个文件名,而是一个操作的名字,属于“伪目标”,作用是删除所有.o文件。

$ make clean

但是,如果当前目录中,正好有一个文件叫做clean,那么这个命令不会执行。因为Make发现clean文件已经存在,就认为没有必要重新构建了,就不会执行指定的rm命令。

为了避免这种情况发生,可以明确声明clean是"伪目标",写法如下

.PHONY : clean
clean:
	rm *.o temp

声明clean是"伪目标"之后,make就不会去检查是否存在一个叫做clean的文件,而是每次运行都执行对应的命令。类似于.PHONY的内置名还有许多,此处暂且不表。

如果Make命令运行时没有指定目标,默认会执行Makefile文件的第一个目标。

$ make

此代码只执行Makefile文件的第一个目标,当然,若此目标中有前置条件,会先满足前置条件而后执行。

前置条件prerequisites

前置条件通常也是一组文件名,之间用空格分隔。它指定了目标是否重新构建的判断标准:只要有一个前置文件不存在,或者有过更新(前置文件的last-modification时间戳比目标的时间戳新),目标就需要重新构建。
比如如下命令:

target.txt : request.txt
	cp request.txt target.txt

上面代码中,构建 target.txt 的前置条件是 request.txt 。如果当前目录中,request.txt 已经存在,那么命令make target.txt可以正常运行,否则必须再写一条规则,来生成 request.txt 。

request.txt:
    echo "this is the source" > request.txt

上面代码中,request.txt后面没有前置条件,就意味着它跟其他文件都无关,只要这个文件还不存在,每次调用make request.txt,它都会生成。

$ make target.txt
$ make target.txt

上面命令连续执行两次make target.txt。第一次执行会先新建 request.txt,然后再新建 target.txt。第二次执行,Make发现 request.txt 没有变动(时间戳早于 target.txt),就不会执行任何操作,target.txt 也不会重新生成。

如果需要生成多个文件,往往采用:

source : file1 file2 file3

在这个代码中,source是一个伪目标,只有三个前置条件(文件),没有任何命令,这样,直接使用命令make source就可以一次性生成三个文件,方便了一些。

命令commands

命令(commands)表示如何更新目标文件,由一行或多行的Shell命令组成。它是构建目标的具体指令,它的运行结果往往就是生成目标文件。

每行命令都是在一个单独的shell中运行,这些shell之间没有关系,因此,对如下代码:

test:
	export foo = bar
	echo "foo=[$$foo]"

执行(make test)后,并不能取到foo的值。因为两行命令是在两个不同的进程之间运行的。要想达到预期效果,可以将两个命令写在一行,中间使用分号隔开,或者在换行符前加反斜杠转义,或者加上 .ONESHELL 命令

#将两个命令写在一行,中间使用分号隔开
test:
    export foo=bar; echo "foo=[$$foo]"
#在换行符前加反斜杠转义
test:
    export foo=bar; \
    echo "foo=[$$foo]"
#加上.ONESHELL:命令
.ONESHELL:
var-kept:
    export foo=bar; 
    echo "foo=[$$foo]"

注意:每行命令前必须要有一个tab键,如果想用其他键,可以使用内部变量.RECIPEPREFIX声明。

.RECIPEPREFIX = >
all:
> echo Hello, world

上面的代码即用.RECIPEPREFIX指定了大于号(>)替代tab键。所以,每一行命令的起首变成了大于号,而不是tab键。

Makefile语法

1.注释
#在Makefile中表示注释
2.回声(echoing)
正常情况下,make会打印每条命令,然后再执行, 此机制叫做回声(echoing),比如

test:
	#just for a test

执行make test,会出现如下结果:

$ make test
#just for a test

而如果在命令的前面加上@,就可以关闭回声,再次执行make test 就不会有任何输出。
由于在构建过程中,需要了解当前在执行哪条命令,所以通常只在注释和纯显示的echo命令前面加上@。

3.通配符
与bash一致,主要有*,?,以及[…]和~。

通配符作用
代表一个任意字符
*代表0个或任意多个字符
[ ]代表括号中的一个字符
[-]-代表一个范围
[^]^代表逻辑非,类似正则表达式规则

4.模糊匹配
Makefile中可以对文件名进行类似正则运算的匹配,主要的匹配符号是%。
5.变量和赋值符
Makefile中允许使用等号来自定义变量。

txt = hello world
test:
	@echo $(txt)

上面的代码中, 变量txt等于hello world,调用时,变量需要放在$()之中。
调用shell变量,需要在美元符号之前再加一个美元符号,因为make命令会对美元符号转义。

test:
	@echo $$HOME

事实上,Makefile中一共提供了四个赋值运算符(=、:=、?=、+=)

var = value
# 在执行时扩展,允许递归扩展
var := value
#在定义时扩展
var ?= value
#只有在该变量值为空时才设置值
var += value
#将值追加到变量的尾端

6.内置变量
make命令中有一系列的内置变量,比如$(CC)指向当前使用的编译器,$(MAKE)指向当前使用的Make命令,更多可见其手册。
7.自动变量
(1)$@
$@指代当前目标,就是Make命令当前构建的那个目标。

a.txt b.txt:
	touch $@

等同于

a.txt b.txt:
	touch a.txt ;  touch b.txt

(2)$<
$<指代第一个前置条件。比如,规则为 t: p1 p2,那么$< 就指代p1。
(3)$?
$? 指代比目标更新的所有前置条件,之间以空格分隔。比如,规则为 t: p1 p2,其中 p2 的时间戳比 t 新,$?就指代p2。
(4)$^
$^ 指代所有前置条件,之间以空格分隔。比如,规则为 t: p1 p2,那么 $^ 就指代 p1 p2 。
(5)$*
$* 指代匹配符 % 匹配的部分, 比如% 匹配 f1.txt 中的f1 ,$* 就表示 f1。
(6)$(@D) 和 $(@F)
$(@D) 和 $(@F) 分别指向 $@ 的目录名和文件名。比如,$@是 src/input.c,那么$(@D) 的值为 src ,$(@F) 的值为 input.c。
(7)$(<D) 和 $(<F)
$(<D) 和 $(<F) 分别指向 $< 的目录名和文件名。
8.判断与循环
使用bash语法完成判断与循环。

ifeq($(CC),gcc)
	libs=$(libs_for_gcc)
else
	libs=$(normal_libs)
endif

上面的代码判断当前编译器是否是gcc,并指定不同的库文件
循环语句

LIST = one two three
all:
    for i in $(LIST); do \
        echo $$i; \
    done

9.函数
Makefile中可以使用许多预定义的函数,使用的方法为

var = $(func_name arg1,arg2,...)
#返回值=$(函数名 实参)

Makefile中预定义函数有很多,包括shell,wildcard,subsr等函数,详见其文档。
由于Makefile中自定义函数本质上是一个多行变量,无法直接调用,因此暂按下不表。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值