Makefile的简单使用使用

Makefile简介

Makefile组成部分

  • Makefile 主要的 5个部分 (显示规则, 隐晦规则, 变量定义, 文件指示, 注释)
  1. 显示规则 :: 说明如何生成一个或多个目标文件(包括 生成的文件, 文件的依赖文件, 生成的命令)
  2. 隐晦规则 :: make的自动推导功能所执行的规则
  3. 变量定义 :: Makefile中定义的变量
  4. 文件指示 :: Makefile中引用其他Makefile; 指定Makefile中有效部分; 定义一个多行命令
  5. 注释 :: 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

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值