Makefile的简单使用使用
Makefile简介
Makefile组成部分
- Makefile 主要的 5个部分 (显示规则, 隐晦规则, 变量定义, 文件指示, 注释)
- 显示规则 :: 说明如何生成一个或多个目标文件(包括 生成的文件, 文件的依赖文件, 生成的命令)
- 隐晦规则 :: make的自动推导功能所执行的规则
- 变量定义 :: Makefile中定义的变量
- 文件指示 :: Makefile中引用其他Makefile; 指定Makefile中有效部分; 定义一个多行命令
- 注释 :: Makefile只有行注释 “#”, 如果要使用或者输出"#"字符, 需要进行转义, "#"
Makefile基本格式
targwt… :prerequisites…
command
…
…
Makefile初级语法
一、规则语法
规则主要有2部分: 依赖关系 和 生成目标的方法.
语法有以下2种:
target ... : prerequisites ...
command
...
或者
target ... : prerequisites ; command
command
...
a.目标target
target:目标文件,生成的文件 可以是 Object File, 也可以是某个操作的名字
伪目标
因为规则语法中的target是可以表示目标文件或者是某个操作的名字,所以有时候会存在文件名跟某个操作名字的冲突
clean:
rm *.o
这里的clean是表示删除文件操作的名字,但是,如果当前目录中,正好有一个文件叫做clean,那么这个命令不会执行。因为Make发现clean文件已经存在,就认为没有必要重新构建了,就不会执行指定的rm命令,为了避免这种情况,我们可以明确声明clean是"伪目标",如
.PHONY: clean
clean:
rm *.o temp
声明clean是"伪目标"之后,make就不会去检查是否存在一个叫做clean的文件,而是每次运行都执行对应的命令。
注意:再执行make命令时,如果Make命令运行时没有指定目标,默认会执行Makefile文件的第一个目标。
$ make
b. 前置条件prerequisites
前置条件prerequisites:生成 target 所需要的文件或者目标
前置条件通常是一组文件名,之间用空格分隔。前置条件的文件通常是当前目录已存在的文件按,或者是下方目标文件。它指定了"目标"是否重新构建的判断标准:只要有一个前置文件不存在,或者有过更新(前置文件的last-modification时间戳比目标的时间戳新),"目标"就需要重新构建。
result.txt: source.txt
cp source.txt result.txt
上面代码中,构建 result.txt 的前置条件是 source.txt 。如果当前目录中,source.txt 已经存在,那么make result.txt可以正常运行,否则必须再写一条规则,来生成 source.txt 。
source.txt:
echo "this is the source" > source.txt
上面代码中,source.txt后面没有前置条件,就意味着它跟其他文件都无关,只要这个文件还不存在,每次调用make source.txt,它都会生成。
test:a.o b.o c.o
gcc -c -o test a.o b.o c.o
a.o:a.c
gcc -c -o a.o a.c
b.o:b.c
gcc -c -o b.o b.c
c.o:c.c
gcc -c -o c.o c.c
这里的前置条件里的文件a.o b.o c.o在目录中是不存在的,但是下方的目标会创建目标文件,同样也是可以的
$ make result.txt
$ make result.txt
上面命令连续执行两次make result.txt。第一次执行会先新建 source.txt,然后再新建 result.txt。第二次执行,Make发现 source.txt 没有变动(时间戳晚于 result.txt),就不会执行任何操作,result.txt 也不会重新生成。
如果需要生成多个文件,往往采用下面的写法。
source: file1 file2 file3
c. 命令command:
命令(commands)表示如何更新目标文件,由一行或多行的Shell命令组成。它是构建"目标"的具体指令,它的运行结果通常就是生成目标文件。
二、 规则中的通配符
* :: 表示任意一个或多个字符
? :: 表示任意一个字符
[...] :: ex. [abcd] 表示a,b,c,d中任意一个字符, [^abcd]表示除a,b,c,d以外的字符, [0-9]表示 0~9中任意一个数字
~ :: 表示用户的home目录
三、变量和赋值符
VARIABLE
=
value
在执行时扩展,允许递归扩展。
VARIABLE:=
value
在定义时扩展。
VARIABLE?=
value
只有在该变量为空时才设置值。
VARIABLE+=
value
将值追加到变量的尾端。
a.变量定义 ( = or := )
OBJS = programA.o programB.o
OBJS-ADD = $(OBJS) programC.o
# 或者
OBJS := programA.o programB.o
OBJS-ADD := $(OBJS) programC.o
其中 = 和 := 的区别在于,
:=
定义的值即可确定,:
定义的值只用到时才确认。 := 只能使用前面定义好的变量, = 可以使用后面定义的变量
变量定义:=
# Makefile内容
OBJS2 := $(OBJS1) programC.o
OBJS1 := programA.o programB.o
all:
@echo $(OBJS2)
# bash中执行 make, 可以看出 OBJS2 中的 $(OBJS1) 为空
$ make
programC.o
变量定义:
# Makefile内容
OBJS2 = $(OBJS1) programC.o
OBJS1 = programA.o programB.o
all:
@echo $(OBJS2)
# bash中执行 make, 可以看出虽然 OBJS1 是在 OBJS2 之后定义的, 但在 OBJS2中可以提前使用
$ make
programA.o programB.o programC.o
b. 变量追加值 (+=)
将值追加到变量的尾端
在这里插入代码片# Makefile内容
abc:= 123 456 789
abc+= 666666
all:
@echo $(abc)
$ make
输出为: 123 456 789 666666
c.变量空时赋值变量 (?=)
只有在该变量为空时才设置值
a ?= 123
b = 666
b? = 888
all:
@echo $(a)
@echo $(b)
# bash中运行make
$ make
123
666
# bash中运行make a=12345678
$ make a=12345678
12345678
666
四、自动变量
Makefile 中很多时候通过自动变量来简化书写, 各个自动变量的含义如下:
自动变量 ——含义
$@ ———— 目标集合
$% ———— 当目标是函数库文件时, 表示其中的目标文件名
$< ———— 第一个依赖目标.如果依赖目标是多个, 逐个表示依赖目标
$? ———— 比目标新的依赖目标的集合
$^ ———— 所有依赖目标的集合, 会去除重复的依赖目标
$+ ———— 所有依赖目标的集合, 不会去除重复的依赖目标
$* ———— 这个是GNU make特有的, 其它的make不一定支持
- $@
$@
指代当前目标,就是Make命令当前构建的那个目标。比如,make foo的$@
就指代foo。
a.txt b.txt:
touch $@
等同于下面的写法
a.txt:
touch a.txt
b.txt:
touch b.txt
- $<
$<
指代第一个前置条件。比如,规则为 t: p1 p2,那么$<
就指代p1。
a.txt: b.txt c.txt
cp $< $@
等同于下面的写法
a.txt: b.txt c.txt
cp b.txt a.txt
- $?
$?
指代比目标更新的所有前置条件,之间以空格分隔。比如,规则为 t: p1 p2,其中 p2 的时间戳比 t 新,$?
就指代p2。
- $^
$^
指代所有前置条件,之间以空格分隔。比如,规则为 t: p1 p2,那么$^
就指代 p1 p2 。
test:a.o b.o c.o
gcc -o test a.o b.o c.o
等同于下面的写法
test:a.o b.o c.o
gcc -o test $^
- $*
$*
指代匹配符 % 匹配的部分, 比如% 匹配 f1.txt 中的f1 ,$*
就表示 f1。
- $(@D) 和 $(@F)
$(@D) 和 $(@F)
分别指向$@
的目录名和文件名。比如,$@
是 src/input.c,那么$(@D)
的值为 src ,$(@F)
的值为 input.c。
- $(<D) 和 $(<F)
$(<D) 和 $(<F)
分别指向 $< 的目录名和文件名。
dest/%.txt: src/%.txt
@[ -d dest ] || mkdir dest
cp $< $@
/*上面代码将 src 目录下的 txt 文件,拷贝到 dest 目录下。首先判断 dest 目录是否存在
如果不存在就新建,然后,$< 指代前置文件(src/%.txt), $@ 指代目标文件(dest/%.txt)*/
完整的Makefile例子
eg:
我们可以这样写,但是若是文件有N个,我们就需要写N条命令,这样就很麻烦,不符合我们的要求
test:a.o b.o c.o
gcc -o test a.o b.o c.o
a.o:a.c
gcc -c -o a.o a.c
b.o:b.c
gcc -c -o b.o b.c
c.o:c.c
gcc -c -o c.o c.c
所以我们这样设置
test:a.o b.o c.o
gcc -o test $^
%.o:%.c
gcc -c -o $@ $<
clean:
rm *.o test
b. 即时变量、延时变量,export
c. 假想目标:.PHONY
五、echo输出变量
A = 0130
B := 456
all:
echo $(A)
echo $(B)
输出为:
echo 0130
0130
echo 456
456
A = 0130
B := 456
all:
@echo $(A)
@echo $(B)
表示不打印命令本身
输出为:
0130
456
六、Makefile文件语法
A. 循环函数foreach
语法:$(foreach <var>,<list>,<text>)
把参数<list>
中的单词逐一取出放到参数<var>
所指定的变量中,然后再执行<text>
所包含的表达式。每一次<text>
会返回一个字符串,循环过程中,
<text>
的所返回的每个字符串会以空格分隔,最后当整个循环结束时,<text>
所返回的每个字符串所组成的整个字符串(以空格分隔)将会是foreach函数的返回值。
# Makefile 内容
targets := a b c d
objects := $(foreach i,$(targets),$(i).o)
all:
@echo $(targets)
@echo $(objects)
# bash 中执行 make
$ make
a b c d
a.o b.o c.o d.o
B. 过滤函数:filter
语法:$(filter PATTERN…,TEXT)
函数功能:过滤掉字串TEXT
中所有不符合模式PATTERN
模式的单词,保留所有符合此模式的单词。可以使用多个模式。模式中一般需要包含模式字符%
。存在多个模式时,模式表达式之间使用空格分割。
返回值:空格分割的TEXT
字串中所有符合PATTERN
模式的字串。
函数说明:filter
函数可以用来去除一个变量中的某些字符串
# Makefile 内容
all:
@echo $(filter %.o %.a,program.c program.o program.a)
# bash 中执行 make
$ make
program.o program.a
C. 反过滤函数:filter-out
语法:$(filter-out PATTERN...,TEXT)
函数功能:和filter
函数实现的功能相反。过滤掉字串TEXT
中所有符合模式PATTERN
的单词,保留所有不符合此模式的单词。可以有多个模式。存在多个模式时,模式表达式之间使用空格分割。。
返回值:空格分割的TEXT
字串中所有不符合模式PATTERN
的字串。
函数说明: filter-out
函数也可以用来去除一个变量中的某些字符串, (实现和filter
函数相反)。
# Makefile 内容
all:
@echo $(filter-out %.o %.a,program.c program.o program.a)
# bash 中执行 make
$ make
program.c
D. wildcard:扩展通配符函数
语法:$(wildcard PATTERN...)
# Makefile内容
all:
a = (wildcard *.c ./test/*.c)
@echo $(a)
搜索当前目录及./test/
下所有以.c
结尾的文件,生成一个以空格间隔的文件名列表,并赋值给a
当前目录文件只有文件名,子目录下的文件名包含路径信息,比如./test/num.c。
E. notdir:去掉目标的路径函数
语法:$(notdir $(sources))
# Makefile内容
a = (wildcard *.c ./test/*.c)
all:
b = (notdir $(a))
b = $(notdir(wildcard *.c ./test/*.c)) ## 更上面一样的效果
去除所有的目录信息,a
里的文件名列表将只有文件名,这里把./test/*.c
里的./test
目录信息去除,剩下*.c
文件名称。
F. patsubst:替换通配符函数
语法:$(patsubst PATTERN...)
#Makefile
src=$(wildcard *.c ./sub/*.c)
dir=$(notdir $(src))
obj=$(patsubst %.c,%.o,$(dir) )
patsubst
是patten substitude的缩写,匹配替代的意思。这句是在dir中找到所有.c
结尾的文件,然后把所有的.c
换成.o
。
七、借鉴链接
链接:https://www.cnblogs.com/wang_yb/p/3990952.html