代码变成可执行文件,叫做编译(compile);先编译这个还是先编译那个(即编译的安排),叫做构建(build)。Make是最常用的构建工具,诞生于1977年,主要用于C语言的项目。
Makefile基础
一、Make
make命令执行时需要一个makefile文件,以告诉make命令需要怎么样的去编译和链接程序。它是一个根据指定的Shell命令进行构建的工具,规则很简单,你规定要构建哪个文件、它依赖哪些源文件,当那些文件有变动时,如何重新构建它。
二、Makefile
Makefile
文件由一系列规则rules
构成,每条规则就明确两件事:构建目标的前置条件是什么,以及如何构建。每条规则的形式如下
1 | <target> : <prerequisites> |
- 目标
target
:一个目标target
就构成一条规则,它通常是文件名(一个或多个,多个用空格分开),指明Make
命令所要构建的对象目标是一/多个文件名。target
除了可以是文件名,还可以是某个操作的名字,这称为”伪目标”(phony target
)。
- 前置条件
prerequisites
:它通常是一组文件名,之间用空格分隔。- 前置条件指定了
目标
是否重新构建的判断标准:只要有一个前置文件不存在,或者有过更新(前置文件的last-modification时间戳比目标的时间戳新)目标
就需要重新构建。
- 前置条件指定了
- 制表键
tab
:每行命令之前必须有一个制表键tab
。- 如果想用其他键可以用内置变量
.RECIPEPREFIX
声明。
- 如果想用其他键可以用内置变量
- 命令
commands
:表示如何更新目标文件,由一行或多行的shell
命令组成。它是构建目标
的具体指令,它的运行结果通常就是生成目标文件。- 每行命令在一个单独的
shell
中执行,这些shell
之间没有继承关系。若要一次性执行执行多行shell
有三种方案:- 将多行shell写在一行,中间用逗号分隔
- 在每行shell后加反斜杠
\
转义 - 使用内置变量
.ONESHELL
- 每行命令在一个单独的
- Makefile语法
- 注释:用
#
,跟shell
脚本一样 - 回声
echoing
:正常情况下make
会打印每条命令,然后再执行,这就叫做回声。- 在命令的前面加上
@
可以关闭回声。
- 在命令的前面加上
- 通配符
wildcard
:用来指定一组符合条件的文件名。Makefile
的通配符与Bash
一致,主要有*
、?
和[...]
,如*.o
表示所有后缀名为o的文件。
- 模式匹配:Make命令允许对文件名进行类似正则运算的匹配,主要用到的匹配符是
%
。- 使用匹配符
%
可以将大量同类型的文件只用一条规则就完成构建。
- 使用匹配符
- 变量和赋值符:和大多数变成语言类似,使用等号
=
进行赋值给变量- 调用
shell
变量需要在美元符号前再加一个美元符号,如echo $$PATH
- 调用
- 注释:用
1 | VARIABLE = value # 在执行时扩展,允许递归扩展 |
- 内置变量
Implicit Variables
:Make命令提供一系列内置变量,比如$(CC)
指向当前使用的编译器,$(MAKE)
指向当前使用的Make工具。 - 自动变量
Automatic Variables
:自动变量的值与当前规则有关,常用的如下:$@
指代当前目标,就是Make命令当前构建的目标。$<
指代第一个前置条件,如规则为t: p1 p2
,那么$<
就指代p1
。$^
指代所有前置条件,之间以空格分隔。$+
表示所有依赖文件的集合(不去重)。$?
指代比目标更新的所有前置条件,之间以空格分隔。$*
指代匹配符%
匹配到的部分。$(@D)
和$(@F)
分别指向$@
的目录名和文件名。$(<D)
和$(<F)
分别指向$<
的目录名和文件名。
- 判断和循环:
Makefile
使用Bash
语法完成判断和循环。 - 函数:
Makefile
还可以使用函数,格式为:$(function arguments)或${function arguments}
,一些常用的函数如下:shell
函数用来执行shell
命令。wildcard
函数用来在Makefile
中替换Bash
的通配符。subst
函数用来文本替换。patsubst
函数用于模式匹配的替换。strip
函数用于去除头部和尾部的空格。filter
函数用于过滤特定模式的字符串。word
函数用于取单词。
三、参考
Makefile中条件判断
一、介绍
使用条件判断可以让make根据运行时的不同情况选择不同的执行分支,条件表达式可以是比较变量的值,或是比较变量和常量的值。
- 语法
1 | ifxxx (arg1,arg2) |
条件常用格式
ifxxx (arg1,arg2)
ifxxx “arg1” “arg2”
ifxxx ‘arg1’ ‘arg2’
ifxxx “arg1” ‘arg2’
ifxxx ‘arg1’ “arg2”
分类
判断 | 作用 |
---|---|
ifeq | 判断参数是否相等,相等为true ,否则为false |
ifneq | 判断参数是否不相等,不相等为true ,否则为false |
ifdef | 判断参数是否有值,有值为true ,否则为false |
ifndef | 判断参数是否没有值,没有值为true ,否则为false |
二、使用
- 定义makefile文件
1 | .PHONY : test |
执行
make
或make test
,正常会输出success
注意:
ifxxx
后面有个空格ifxxx
:当ifxxx
没有缩进时make会正确识别它们,将其作为分支选择的标识;当ifxxx
有缩进时,make将它们当做普通的shell script
。- 意思就是ifxxx前不用加tab
三、参考
Makefile中自动变量与隐晦规则
一、自动变量
makefile中的自动变量实质上是对一类变量的简写,当我们在模式规则中对这类变量处理的时候可以直接使用自动变量简化makefile代码的编写。自动变量包括如目标文件,依赖文件等,常用的自动变量:
$@
指代当前目标,就是Make命令当前构建的那个目标。- makefile内容如下
1
2test:
@echo $@- 执行
make
得到结果test
,即目标本身
$<
指代第一个前置条件。- makefile内容如下
1 | test.txt: source.txt result.txt |
- 执行
make
或make test.txt
得到结果source.txt
$^
指代所有前置条件,之间以空格分隔。- makefile内容如下
1 | test.txt: source.txt result.txt |
- 执行
make
或make test.txt
得到结果source.txt result.txt
$+
表示所有依赖文件的集合(不去重),$^
会去重。$?
指代比目标更新的所有前置条件,之间以空格分隔。$*
指代匹配符%
匹配到的部分。$(@D)
和$(@F)
分别指向$@
的目录名(相对路径)和文件名。$(<D)
和$(<F)
分别指向$<
的目录名(相对路径)和文件名。
二、隐晦规则
使用makefile
隐晦规则可以省去为每一个类似的规则生成都去写类似的规则,makefile
会自动推导依赖文件,并根据隐含规则推导出生成当前目标的命令。
- 不使用隐晦规则
1 | target: a.o b.o c.o |
- 使用隐晦规则
1 | SOURCE := $(shell find ./ -type f -name *.c) |
执行过程:
makefile
中首先声明了变量SOURCE
和OBJECTS
分别代表当前文件夹下的.c
和.o
文件集合。目标target
依赖所有的目标文件.o
,即a.o,b.o,c.o
。当需要依赖a.o
时makefile
会根据隐晦规则自动推导出生成a.o
文件的命令g++ -c $(CFLAGS) -o a.o a.c
生成a.o
,同理会根据隐晦规则生成b.o、c.o
,三个.o
文件生成后再根据上述规则生成target
。
三、参考
Makefile中伪目标与PHONY
一、概念
Makefile
文件由一系列规则rules
构成,规则是有格式要求的,其中最主要的就是目标文件target
,其次就是前置条件prerequisites
和命令commands
(二者至少存在一个)。当目标文件target
不是一个文件时就称此target
为伪目标。
.PHONY
是一个特殊的目标(special target
),也可称为伪目标,它用来指定一个假想的工作目标,其后面并不是一个实际文件,而是一个必须要执行的命令名。即存在.PHONY
时,其后面的命令一定会执行,可以有效防止在Makefile
文件中定义的可执行命令的目标规则和工作目录下的实际文件出现名称冲突,并提高Makefile
的执行性能。
二、使用
- 不带
.PHONY
- 不存在同名文件
clean
,执行make clean
会正常删除result.txt
- 存在同名文件
clean
,执行make clean
会报错
- 不存在同名文件
1 | clean: |
- 带
.PHONY
1 | clean: |