Makefile笔记

Makefile里有什么

Makefile里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释。

  1. 显式规则。显式规则说明了,如何生成一个或多个目标文件。这是由Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。
  2. 隐晦规则。由于我们的make有自动推导的功能,所以隐晦的规则可以让我们比较简略地书写Makefile,这是由make所支持的。
  3. 变量的定义。在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点像你C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。
  4. 文件指示。其包括了三个部分,(1)在一个Makefile中引用另一个Makefile,(2)根据某些情况指定Makefile中的有效部分,就像C语言中的预编译#if一样,(3)定义一个多行的命令。
  5. 注释。注释是用“#”字符,如果你要在你的Makefile中使用“#”字符,可以用反斜杠进行转义,如:“#”。

规则

关于目标与依赖新旧比较

main:
	cmd

如果main不存在,则永远执行。

如果main存在,则不执行。

.PHONY: clean
clean:
	cmd

开始目录下还没有clean文件,每次clean都能执行。

突然目录下生成了一个clean文件,导致clean不执行了。

我的本意就没打算把clean作为一个文件,它就是纯目标,那就加到.PHONY后面,表示它是一个伪目标,这样clean每次都会执行。

main: main.c
	cmd

如果main不存在,则默认比依赖旧,会执行。

如果main存在,则与依赖比较新旧。

自动推导

只要make看到一个[.o]文件,它就会自动的把[.c]文件加在依赖关系中,如果make找到一个whatever.o,那么 whatever.c,就会是whatever.o的依赖文件。并且 cc -c whatever.c 也会被推导出来,于是,我们的makefile 再也不用写得这么复杂。

简写Makefile:

main: foo.o

执行make:

$ make
cc    -c -o foo.o foo.c
cc     main.c foo.o   -o main

引用其他的Makefile

include mks/a.mk b.mk

命令

命令前缀

@rm -f *.o	#不会输出命令
rm -f *.o	#会输出命令
-rm -f *.o	#忽略出错命令

嵌套执行make

export VERSION = 1.0
subsystem:
        make -C subdir

先进入“subdir”目录,然后执行make命令。并传递变量到下级Makefile中。

定义命令包

定义:

define run-yacc
yacc $(firstword $^)
mv y.tab.c $@
endef

使用:

foo.c : foo.y
        $(run-yacc)

就好像使用变量一样。在这个命令包的使用中,命令包“run-yacc”中的“ ” 就是“ f o o . y ”,“ ^”就是“foo.y”, “ 就是foo.y@”就是“foo.c”。

变量

定义/使用变量

定义变量:

objects = program.o foo.o utils.o

使用变量:

a = $(objects)
b = ${objects}

变量赋值

"="表示延迟赋值

x = foo
y = $(x) # y=bar
x = bar

":="表示直接赋值

x := foo
y := $(x) # y=foo
x := bar

"?="表示如果该变量没有被赋值,则赋予等号后面的值

x ?= foo # x=foo
x ?= bar # x=foo

"+="表示将符号后面的值添加到前面的变量上

x = foo  # x=foo
x += bar # x=foobar

"override"表示覆盖make命令行参数设置的变量

override <variable> = <value>

变量值的替换

foo := a.o b.o c.o
bar := $(foo:.o=.c)

把变量“var”中所有以“a”字串“结尾”的“a”替换成“b”字串。这里的“结尾”意思是“空格”或是“结束符”。

把变量的值再当成变量

x = y
y = z
a := $($(x))#a=z

局部变量

t1: x=foo
t1: tt1 tt2
	@echo $(x)
tt1:
	@echo $(x)
tt2:
	@echo $(x)

变量x会作用到由目标t1所引发的所有的规则中去。

可以以如下方式给所有以[.o]结尾的目标定义目标变量:

%.o: CFLAGS = -O
foo.o: foo.c
	gcc $(CFLAGS) ...
bar.o: bar.c
	gcc $(CFLAGS) ...

自动化变量

$@表示目标文件

$^表示所有的依赖文件

$<表示第一个依赖文件

$?表示所有比目标新的依赖文件的集合

函数

调用函数格式:

$(function arg1,arg2)
# 或者
${function arg1,arg2}

字符串处理函数

# 匹配目录下所有.c 文件
$(wildcard ./*.c)
# 把字串<text>中的<from>字符串替换成<to>
$(subst <form>,<to>,<text>)
# 模式字符串替换函数
$(patsubst %.c,.o,<text>)
# 去掉开头个结尾的空字符
$(strip <string>)
# 查找字符串,找到则返回<find>,找不到则返回空字符串
$(findstring <find>,<in>)
# 以<pattern>模式过滤<text>字符串中的单词,保留符合模式<pattern>的单词。可以有多个模式。
$(filter <pattern...>,<text>)
# 以<pattern>模式过滤<text>字符串中的单词,去除符合模式<pattern>的单词。可以有多个模式。
$(filter-out <pattern...>,<text>)
# 给字符串<list>中的单词排序(升序)。sort函数会去掉<list>中相同的单词。
$(sort <list>)
# 取字符串<text>中第<n>个单词。(从一开始)找不到则返回空字符串。
$(word <n>,<text>)
# 从字符串<text>中取从<ss>开始到<e>的单词串。<ss>和<e>是一个数字。
$(wordlist <ss>,<e>,<text>)
# 单词个数统计函数
$(words <text>)

文件名操作函数

# 从文件名序列<names>中取出目录部分。目录部分是指最后一个反斜杠(“/”)之前的部分。如果没有反斜杠,那么返回“./”。
$(dir <names...>)
# 从文件名序列<names>中取出非目录部分。非目录部分是指最後一个反斜杠(“/”)之后的部分。
$(notdir <names...>)
# 从文件名序列<names>中取出各个文件名的后缀。
$(suffix <names...>)
# 从文件名序列<names>中取出各个文件名的前缀部分。
$(basename <names...>)
# 把后缀<suffix>加到<names>中的每个单词后面。
$(addsuffix <suffix>,<names...>)
# 把前缀<prefix>加到<names>中的每个单词后面。
$(addprefix <prefix>,<names...>)
# 连接函数,如$(join aaa bbb , 111 222 333)返回值是“aaa111 bbb222 333”。
$(join <list1>,<list2>)

foreach 函数

names := a b c d
files := $(foreach n,$(names),$(n).o)

上面的例子中, ( n a m e ) 中的单词会被挨个取出,并存到变量“ n ”中,“ (name)中的单词会被挨个取出,并存到变量“n”中,“ (name)中的单词会被挨个取出,并存到变量n中,(n).o”每次根据“ ( n ) ”计算出一个值,这些值以空格分隔,最后作为 f o r e a c h 函数的返回,所以, (n)”计算出一个值,这些值以空格分隔,最后作为foreach函数的返回,所以, (n)计算出一个值,这些值以空格分隔,最后作为foreach函数的返回,所以,(files)的值是“a.o b.o c.o d.o”。

if 函数

$(if <condition>,<then-part>)
# 或者
$(if <condition>,<then-part>,<else-part>)

if函数的返回值是,如果<condition>为真(非空字符串),那个<then-part>会是整个函数的返回值,如果<condition>为假(空字符串),那么<else-part>会是整个函数的返回值,此时如果<else-part>没有被定义,那么,整个函数返回空字串。

call 函数

call函数是唯一一个可以用来创建新的参数化的函数。

$(call <expression>,<parm1>,<parm2>,<parm3>,...)

当make执行这个函数时,<expression>参数中的变量,如 ( 1 ) , (1), (1)(2),$(3)等,会被参数<parm1><parm2><parm3>依次取代。而<expression>的返回值就是 call 函数的返回值。例如:

reverse =  $(1) $(2)
foo = $(call reverse,a,b)

那么,foo的值就是“a b”。

origin 函数

告诉你你的这个变量是哪里来的?

$(origin <variable>)

origin函数的返回值有这几种:

undefined:未定义
default:是一个默认的定义,比如“CC”这个变量
environment:是一个环境变量,并且当Makefile被执行时,“-e”参数没有被打开
file:变量被定义在Makefile中
command line:变量是被命令行定义的
override:是被override指示符重新定义的
automatic:是一个命令运行中的自动化变量

shell 函数

contents := $(shell cat foo)
files := $(shell echo *.c)

控制make的函数

$(error <text ...>;)
$(warning <text ...>;)

产生一个致命的错误,<text …>;是错误信息。注意,error函数不会在一被使用就会产生错误信息,所以如果你把其定义在某个变量中,并在后续的脚本中使用这个变量,那么也是可以的。

示例一:

ifdef ERROR_001

$(error error is $(ERROR_001))

endif

示例二:

ERR = $(error found an error!)

.PHONY: err

err: ; $(ERR)

示例一会在变量ERROR_001定义了后执行时产生error调用,而示例二则在目录err被执行时才发生error调用。

条件判断

ifeq

name=foo

show:
ifeq ($(name),foo)
	@echo true
else
	@echo false
endif

ifneq

name=foo

show:
ifneq ($(name),foo)
	@echo true
else
	@echo false
endif

ifdef

name=foo

show:
ifdef name
	@echo true
else
	@echo false
endif

ifndef

name=foo

show:
ifndef name
	@echo true
else
	@echo false
endif

运行make

指定Makefile

所有指定的makefile将会被连在一起传递给make执行。

make -f zc.mk -f zc2.mk

检查规则

有时候,我们不想让我们的makefile中的规则执行起来,我们只想检查一下我们的命令,或是执行的序列。于是我们可以使用make命令的下述参数:

(1)“-n” “–just-print” “–dry-run” “–recon” 不执行参数,这些参数只是打印命令:

name=foo

show:
ifeq ($(name),foo)
	@echo true
else
	@echo false
endif

运行:

$ make show -n
echo true

(2)“-t” “–touch” 这个参数的意思就是把目标文件的时间更新,但不更改目标文件。也就是说,make假装编译目标,但不是真正的编译目标,只是把目标变成已编译过的状态。

$ make mks -t
make: “mks”已是最新。

(3)“-q” “–question” 这个参数的行为是找目标的意思,也就是说,如果目标存在,那么其什么也不会输出,当然也不会执行编译,如果目标不存在,其会打印出一条出错信息。

$ make dsasd -q
make: *** 没有规则可制作目标“dsasd”。 停止。

(4)“-W <file>” “--what-if=<file>” “--assume-new=<file>” “--new-file=<file>” 这个参数需要指定一个文件。一般是是源文件(或依赖文件),Make会根据规则推导来运行依赖于这个文件的命令,一般来说,可以和“-n”参数一同使用,来查看这个依赖文件所发生的规则命令。

t1: t2
	@echo t1
t2: mks
	@echo t2

运行:

$ make -W mks -n t1
echo t2
echo t1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值