规则的命令是由一些shell命令行组成,被一条一条执行。
通常系统中存在不同的shell,但在make处理Makefile过程中,如果没有明确指定,那么对所有规则中的命令行解析使用 “/bin/sh” 完成。
1. 命令回显
回显是指在执行命令之前将要执行的命令输出到标准输出设备。
@参数:
如果命令行以 @ 开始,则make在执行这个命令时不会回显这个要被执行的命令。
比如:
@echo 开始编译
输出为:开始编译
echo 开始编译
输出为:echo 开始编译
-n 或 --just-print参数:
make在执行时只显示所要执行的命令,但不会真正去执行这些命令。
-s 或 --slient参数:
禁止所要执行命令的显式,就好像所要的命令均使用@开始一样。
2. 命令的执行
2.1 独立执行
当多行命令时,每一行的命令将在一个独立的子shell进程中执行,执行是相互独立的。
同一行的多个命令属于一个完整的shell命令行。
2.2 并发执行
使用-j 或者 --job使得make在同一时刻可以允许多条命令同时执行。
并发执行的问题:
- 多个同时执行的命令输出信息将同时被输出到终端。
- 同一时刻多个执行命令的进程中只能有一个进程获得标准输入,其他需要读取的标准输入流的进程由于输入流无效而导致错误。
3. 命令执行的错误
如果一个规则中的某一个命令出错,make就会放弃对当前规则后续命令的执行,也有可能会终止所有规则的执行。
为了忽略一些无关命令执行失败的情况:
- 在命令之前加 “-” 表示忽略此命令的执行失败。
- 使用 -i 或者 --ignore-errors,使得make忽略所有规则中命令执行时错误。
- 使用 -k 或者 --keep-going,使得在出现错误时不立即退出,而是继续后续命令的执行,直到执行到最后链接时才有异常退出。
4. 中断make的执行
make在执行命令时如果收到一个致命信号,那么make将会删除此过程中已经重建的那些规则的目标文件。
原因:
如果中断信号关闭了编译器,则生成的foo.o可能是不完整的,当时这个不完成的foo.o文件的时间戳比源程序foo.c时间戳新,如果不删除foo.o, 则下次执行make时此文件被认为已经是最新的而不去重建。
5. make的递归执行
make递归执行是指在makefile中使用make作为一个命令来执行本身或者其他makefile文件的过程。
在make递归调用时,规则的命令行中应该使用变量 MAKE 来代替 make。优点是当我们使用一个其他版本的make程序时,可以保证最上层使用的make程序和其子目录下执行的make程序保持一直。
subsystem:
cd subdir && $(MAKE)
等价于进入子目录执行:
subsystem:
$(MAKE) -C subdir
6. 变量和递归
在make执行过程中 ,使用环境变量传递上层所定义的变量时,不会覆盖子make中Makefile文件中的同名变量定义。
若存在重复,则以子Makefile中为准,除非使用-e选项。
若需要向变量传递给子make,需要export进行指定。
export VAR
- 若不使用export进行指定,上层make只将那些已经初始化的环境变量 和 使用命令行指定的变量(make CFLAGS += -g) 传递给子make程序。
- 存在特殊变量SHELL 和 MAKEFLAGS ,这两个变量除非使用unexport进行声明,它们在整个make执行过程中始终自动传递给子make。
- 当不希望传递MAKEFLAGS,需要在调用子make时对这边变量进行赋空:
sub:
cd subdir && $(MAKE) MAKEFLAGS=
- 一个不带参数的export指示符表示将Makefile中定义的所有变量传递给子make。如果不需要其中某个,使用unexport单独指定,但是不带参数的unexport没有意义。
export
7. -w选项
在多级make递归调用过程中,使用-w 或者 --print-directory让make在开始编译一个目录之前和完成目录编译之后给出相应的提示信息,方便跟踪。
通常-w会被默认打开。可以使用 -s 禁止此选项。
比如,在目录/u/gun/make 目录下执行make -w:
在执行开始之前看到:
make:Entering directory '/u/gun/make'
在执行完成之后看到:
make:Leaving directory '/u/gun/make'
8. 定义命令包
使用define定义一组命令作为命令包,实现类似于函数的功能。
在命令包中,命令体中的变量和函数的引用不会展开,在规则被使用的时候完全展开。
比如:
define run-yacc
yacc $(firstword $^)
mv y.tab.c $@
endef
使用:
foo.c:foo.y
$(run-yacc)
替换过程:
命令包中的 $^ 会被 foo.y替换
$@会被foo.c替换