Makefile教程

1 Makefile语法和规则

语法:

targets : prerequisites
	command
	...

targets : prerequisites ; command
	command

其中:

  • target为生成目标, 一般使用文件名或伪目标作为target, 多个target用空格分开,可使用通配符。 除了指定要生成的target, 其他target若没有被依赖则不会执行。要生成的target通过make target命令指定, 不指定则第一个target为默认target。

  • prerequisite可以是target、文件, 若prerequisite有同名的target, 会先按规则执行同名target下的command。

  • command 如果在新行必须以Tab开头; 若一行有多条语句, 用分号;分隔。(以Tab开头的都会被当做command, 所以Makefile其他语句前不能使用Tab, 排版应使用空格)

    command结果只对当前行有效(每行command处于不同进程下)。例子:

    exec:
    	cd /home/raina
    	pwd  # 没有切换路径, 仍是当前目录
    
    exec:
    	cd /home/raina; pwd  # 成功切换到/home/raina 
    

规则:

  • 仅当target不存在或prerequisite中有一个文件比target新时才会执行下面的command。
  • 没有prerequisite的target仅当target不存在时command才执行(注意自动推导可能会为target添加依赖)。

2 make运行

Makefile使用make命令运行, 将Makefile文件名保存成Makefilemakefile, 然后在终端运行make命令, 会寻找当前目录下的Makefilemakefile文件, 自动运行。

2.1 make的选项

make -n  
	# 仅显示命令, 不执行命令
make -s
	# 禁止所有命令语句本身的显示
make -f xxx
	# 指定makefile文件为xxx
make -t
	# 更新目标文件时间
make -q
	# 寻找目标是否存在, 存在不会输出, 不存在打印错误信息
make -B
	# 所有目标都需要重新编译
make -c dir
	# 指定读取Makefile的目录
make -e
	# 环境变量将覆盖Makefile内定义的变量
make -I dir
	# 添加头文件查找路径
make -r
	# 禁止make任何隐含规则
make -R
	# 禁止make使用任何作用于变量上的规则

2.2 make执行过程

Makfile文件内容:

test : main.o
	gcc -o test main.o
main.o : main.c
	gcc -c -o main.o main.c
  1. make会在当前目录下寻找“Makefile”或“makefile”文件
  2. 找到后会找到Makefile中第一个target,也就是上面例子中的test
  3. test的依赖main.o有对应的生成规则, 转去main.o:main.c
  4. 如果main.o不存在, 或当前目录下的main.c比main.o新, 就会执行下面的gcc -c -o main.o main.c; 若main.o比main.c新, 下面给的command就不会执行
  5. 若执行了main.o下的命令, 说明main.o已经更新了, test下的命令就会执行; 若main.o没有重新生成, 则比较当前目录下的main.o和test, 若main.o更新则执行gcc -o test main.o, 否则不执行。

在make的过程中,如果没有依赖的生成规则, 路径下也没有该依赖文件,或者command执行出错,make会直接退出。

3 Makefile特殊字符

  • 单行注释: #

  • 换行符: \

  • 转义字符: \ (若要转义’\(‘符号, 需要用`\)$`表示)

  • 通配符: Makefile支持shell的*?[...]通配符;

  • 模式匹配符: %

  • 不显示命令: @, 执行的command语句默认会被显示到中断, 若不希望make执行过程中显示某command, 则在前面添加@符号:

    all: 
    	@echo hhhhhh
    

4 变量

4.1 变量的定义和使用

定义:

var = value

变量在声明时必须赋初值, Makefile没有数据类型, 变量的定义类似与C/C++的宏, 但是可以改变其值。

例子:

# 若当前目录下有a.o、b.o、c.o三个文件
objs = *.o
# objs的值就是*.o, 而不是通配符展开后的结果

Makefile变量定义的位置没有要求, 可以使用后面定义的变量。

变量的命名规则:

  • 变量名可以使用字符、数字、下划线, 可以以数字开头
  • 变量名不能包含: # = 空字符
  • 变量名大小写敏感

**使用: **

$var
$(var)
${var}

若使用没有定义的变量, 其值将为空。

4.2 多行值的变量

若变量的值有多行, 使用define ... endef来定义:

define var  # var为变量名
val-part1  # var的值
val-part2
...
endef

4.3 命令行变量

在命令行定义变量:

make var=value

注意:

  • 等号两边不能加空格;

  • 若Makefile文件中有和命令行变量同名的变量, 在Makefile中改变该变量值的语句会被忽略(包括define的多行值的变量和目标变量);

  • 在Makefile若要修改命令行变量的值, 需在前面添加override 提示符:

    override var=value
    

4.4 自动化变量

自动化变量 说明
$@ 表示target
$< 表示第一个prerequisite
$^ 所有的prerequisite的集合, 空格分隔, 重复的prerequisite会合并
$+ 所有的prerequisite, 空格分隔, 重复的prerequisite不合并
$? 所有比target新的prerequisite的集合, 以空格分隔

4.5 模式变量

模式变量: 包含%的变量。

使用:

类似与shell的*通配符:

%.o : CFLAG = -o  # 定义一个目标变量, 作用于以’.o‘结尾的target

Makefile静态模式规则:

%.o: %.c  # %.c中%的值为target与%.o匹配到的%的值
    gcc -c  $< -o $@
OBJS = a.o b.o

all: $(OBJS) c.o

# 仅对OBJS内的元素有效, c.o不会匹配此规则
$(OBJS): %.o: %.c  # a.o的依赖为a.c, b.o的依赖为b.c
	gcc -c  $< -o $@

4.6 目标变量

目标变量仅在作用的target范围内有效, 类似于局部变量。

定义目标变量只需在变量定义前加其作用的target和冒号:

main : CFLAGS = -o  # 仅在main目标范围内有效
main.o : CFLAGS = -c -o  # 仅在main.o目标范围内有效

main : main.o
	gcc $(CFLAGS) main main.o  # gcc -o main main.o
	
main.o : main.c
	gcc $(CFLAGS) main.o main.c  # gcc -c -o main main.o

4.7 内置变量

$(CURDIR): 当前所在路径

$(MAKE_VERSION): make的版本号

5 赋值运算符

= 基本的赋值, 若变量后面被重新赋值, 会被覆盖

x = 1
y = $(x)
all:
	echo $(y)  # 2
x = 2

:= 若右值含变量, 则使用当前位置该变量的值(而不是整个Makefile展开后的值)

x = 1
y := $(x)
all:
	echo $(y)  # 1
x = 2

+= 追加变量的值(会用空格分隔)

var = a
var += b
all:
	echo $(var)  # a b

6 伪目标

.PHONY用于标记伪目标, 被标记为伪目标的target, 即使当前路径下target已经存在且为最新, 也会执行下面的command。即伪目标一经调用, 一定执行。

例子:

.PHONY: clean cleanobj  # 标记clean和cleanobj为伪目标
clean: cleanobj
	rm -f main
cleanobj:
	rm -f *.o

常见伪目标:

  • all : 编译所有目标
  • clean: 删除所有被make创建的文件
  • install: 安装已编译好的程序, 将目标执行文件拷贝到指定路径
  • print: 列出改变过的源文件
  • dist: 创建一个压缩文件, 一般是将tar压缩成gz文件
  • TAGS: 更新所有目标, 以完整地重新编译使用
  • check或test: 测试makefile流程
  • tar: 将源程序打包备份

7 条件判断

判断已定义:

ifdef var-name  # var-name为变量名, 不加$
  ... # 不能用Tab锁紧, 只能使用空格
else  # else分支可以没有
  ...
endif

判断未定义:

ifndef var-name
  ... 
else
  ...
endif

判断相等:

ifeq (arg1, arg2)
# 或 ifeq 'arg1' 'arg2'
# 或 ifeq "arg1" "arg2"
  ... 
else
  ...
endif

判断不相等:

ifneq (arg1, arg2)
# 或 ifneq 'arg1' 'arg2'
# 或 ifneq "arg1" "arg2"
  ... 
else
  ...
endif

注意: make 是在读取 Makefile 时就计算条件表达式的值, 并根据条件表达式的值来选择语句, 所以, 最好不要把自动化变量(如“$@”等)放入条件表达式中, 因为自动化变量是在运行时才有的。

8 函数的使用

使用方法:

$(func args)

${func args}

常用函数:

$(subset from, to, text)
# 功能: 把字串text中的from字符串替换成to
# 返回值: 被替换后的字符串
$(patsubst pattern, replacement, text)
# 功能: 把text中符合pattern的元素替换成replacement
# 返回值: 被替换后的字符串
$(dir paths)
# 功能: 取出每个path的所在目录(最后一个/及之前的内容, 没有/为当前目录)
#       多个path用空格分开
# 返回值: 返回所在目录, 当前目录返回./
$(notdir paths)
# 功能: 取出每个path的文件命名
# 返回值: 取到的文件名

此文原创禁止转载,转载文章请联系博主并注明来源和出处,谢谢!
作者: Raina_RLN https://www.cnblogs.com/raina/

posted @ 2020-02-21 16:58  Raina_R  阅读(1372)  评论(0编辑  收藏  举报