Last Updated: 2023-09-04 13:41:25 Monday
-- TOC --
make就像个厨师,按照既定规则,先加工原料,然后将原料加工成目标结果。稍微多几个源文件的软件项目,都离不开make。有一些简化版的make工具,比如cmake,qmake,autoconf等,这些工具的最终结果,就是输出一个Makefile,然后被make使用。
make并不复杂,它的规则是比较多,但常用的就那些,学会之后,做项目看源码倍感轻松。
基本规则:
# comments
target: prerequisites
<tab> shell-commands
target是目标,prerequisites是前置条件,也就是原料,shell-commands是达成目标的方法(默认在command前使用tab)。每当命令行出现:
$ make [target]
make首先检查target是否已经存在(在文件目录中是否存在),如果不存在,就必然要执行commands生成。如果target已经存在,make就会检查prerequisites,如果其中有一个前置条件的mtime比target更新,此时就要重新执行commands,重新生成target。如果有部分prerequisites不存在,Makefile中就必须有生成此prerequisite的规则,而make会先生成prerequisite,然后再重新生成target(此时prerequisite的mtime一定更新)。
#
后面是注释。*.o
。make执行时,默认读取当前目录下的Makefile
文件(或小写的makefile
,建议保持M大写,Linux内核貌似只能用大写的Makefile),也可以通过-f
参数指定规则文件:
$ make -f rules.txt
# Or
$ make --file=rules.txt
# Or
$ make --makefile=rules.txt
如果命令行中没有指定target,默认target为Makefile中的第一个target!
基本上tab符号都会被程序员替换成4个空格,输入tab有时并不容易,此时可以用.RECIPEPREFIX
指令来指定一个符号替换tab:
.RECIPEPREFIX = >
target: prerequisites
> shell-commands
通过.PHONY
定义伪目标。
所谓伪目标,即不会在目录中存在的target,对于phony target,make不会检查其是否存在,直接进入下一步。
.PHONY: clean
clean:
rm -f *.o
clean就是个phony target,不管clean是否是一个目录中存在的文件,make不做检查,make clean都会执行rm(无prerequisites)。加入目录中真有一个文件名为clean,也不会影响make这个伪目标。
phony target可以有prerequisites,常见用法如下:
.RECIPEPREFIX = >
.PHONY: all clean
all: abc.txt 123.txt
clean:
> rm -f abc.txt 123.txt
abc.txt: 123.txt
> cat -n 123.txt > abc.txt
123.txt:
> echo 'abc' > 123.txt
make时,默认target是all,all是phony target,此时就成了确保all的两个prerequsites必须存在(target all没有command),而生成它俩的规则在后面。
加速编译:
$ make -j8 <target> # 8 jobs
$ make -j <target> # infinite jobs
多进程时,可能存在一些错误不能及时被发现,我个人的经验是:此时用make,不带-j,就能重现错误。
假设目录下有两个.c文件,分别是a.c和b.c,make规则可以写成这样:
# 为每一个.c文件设立一个生成对应.o文件的规则
%.o: %.c
等同于:
a.o: a.c
b.o: b.c
Makefile中可以直接定义变量,这些变量的值就是一段字符串,可以在command中被引用替换,注意command中引用Makefile中定义的变量,和引用shell环境变量,语法上有点不同:
.PHONY: test
var = 4096! 1234. # define var which value is '4096! 1234.'
test:
echo $(var) # var in makefile
echo $$PATH # env PATH
$(var)
$$PATH
给变量赋值,有以下4种情况(=
,:=
,?=
,+=
):
# Lazy Set
# Normal setting of a variable, but any other variables mentioned
# with the value field are recursively expanded with their value
# at the point at which the variable is used, not the one it had
# when it was declared
VARIABLE = value
# Immediate Set
# Setting of a variable with simple expansion of the values inside
# - values within it are expanded at declaration time.
VARIABLE := value
# Lazy Set if Absent
# Setting of a variable only if it doesn't have a value.
# Value is always evaluated when VARIABLE is accessed.
VARIABLE ?= value
# Append
# Appending the supplied value to the existing value
# (or setting to that value if the variable didn't exist)
VARIABLE += value
示例:
var := 4096! 1234.
v1 := $(var)
v2 := $(v1)
test:
echo $(v2)
v2具有与var完全一样的值。
make的规则,处处体现了递归,比如原料也是目标。
make提供了一系列的内置变量:
$(CC)
表示默认的编译器,即cc(一般指向gcc)$(MAKE)
表示make自己$@
,当前目标
$(@D)
,当前目标的目录名
$(@F)
,当前目标的文件名
$<
,第1个前置条件
%.o : %.c
$(CC) $(CFLAGS) -o $@ $<
$(<D)
,第1个前置条件的目录名
$(<F)
,第1个前置条件的文件名
dest/%.txt: src/%.txt
@[ -d dest ] || mkdir dest
cp $< $@
$^
,所有前置条件
$?
,比target更新的所有前置条件,用空格分开
$*
,%匹配的部分
每一行command,都在一个独立的shell中执行,这些shell之间没有继承关系(都由make创建)。
如果希望多个命令在一个shell进程中执行,有下面两个方法:
;
隔开,可用\
换行。.ONESHELL
指令。.ONESHELL:
test:
export AA=123
echo $$AA
默认情况下,make会打印出它执行的command都有哪些,也可以用@
符号抑制这个打印。将此符号放在command前即可。
test:
@echo 123abc
make提供了很多内置函数,它们需要在$()
或${}
中执行。
shell,执行shell命令
src := $(shell echo *.c)
src := ${shell echo *.c}
wildcard,按shell通配符解释
src := $(wildcard *.c)
subst,替换文本
替换规则:
$(subst from,to,text)
例如:
# 将ee替换成EE
var := $(subst ee,EE,feet on the street)
patsubst,基于pattern的文本替换
规则:
$(patsubst pattern,replacement,text)
关键是使用make的-C
参数:先改变工作路径到指定目录,然后再读取目录中的Makefile文件并开始执行。
$ make -C test_make
make: Entering directory '/home/xinlin/test/test_make'
echo 'abc' > 123.txt
cat -n 123.txt > abc.txt
make: Leaving directory '/home/xinlin/test/test_make'
$ make -C test_make clean
make: Entering directory '/home/xinlin/test/test_make'
rm -f abc.txt 123.txt
make: Leaving directory '/home/xinlin/test/test_make'
本文链接:https://cs.pynote.net/sf/c/cdm/202307042/
-- EOF --
-- MORE --